We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Trait Objects and Dispatch step 8 of 10
Trait objects and lifetimes: Box<dyn Trait + 'a>
Build two tagger factories: one whose closure borrows a prefix, and one whose closure owns it. Then apply both taggers to the same inputs.
pub fn make_tagger(prefix: &str) -> Box<dyn Fn(&str) -> String + '_>
pub fn make_static_tagger(prefix: String) -> Box<dyn Fn(&str) -> String>
pub fn run(prefix: String, inputs: Vec<String>) -> (Vec<String>, Vec<String>)
Each call produces the prefix, a colon, and the input, exactly as
format!("{prefix}:{s}") does. Do not trim or otherwise transform either
string. Empty prefixes and inputs still get the colon.
run returns results from the borrowing tagger first and the owning tagger
second. Preserve input order and duplicates. Both result vectors have the
same contents; an empty input vector produces two empty vectors.
| Prefix | Inputs | Each result vector |
|---|---|---|
"log" |
["a", "bb"] |
["log:a", "log:bb"] |
"" |
["x", ""] |
[":x", ":"] |
"a:" |
[":b"] |
["a:::b"] |
Keep the factory signatures and their compile-time checks. The grader also
checks those types independently. Type compatibility does not prove how a
closure obtained its data: implement the borrowing factory by retaining the
input reference, and the owning factory by retaining the input String.
Why the starter's return type fails
The starter's borrowing factory omits the + '_ bound. In this signature,
Box<dyn Fn(&str) -> String> defaults to a 'static object bound. A closure
that retains an arbitrary input reference cannot meet that bound.
Adding + '_ asks Rust to infer the object bound from the input lifetime.
Here there is one input reference, so the returned type can retain that
borrow. This does not extend the prefix's lifetime; callers must keep its
data valid while the tagger may use it.
This default is specific to the surrounding type and signature. It is not
a rule that every trait object, or every locally inferred box, must be
'static. The default object lifetime rules
describe how context changes the inferred bound.
'static does not mean “never dropped”
The owning factory can meet the 'static object bound because its closure
owns a String instead of borrowing a caller's local string. The box can
still be dropped as soon as the caller finishes with it. The bound does not
make it live for the duration of the process or put it in static storage.
A type satisfying 'static may contain owned data or references to static
data; it cannot depend on a shorter-lived borrow. In this exercise, moving
the owned prefix into the closure gives it the storage it needs.
The prefix borrow and each call argument
The object's lifetime bound concerns what the stored closure captures.
The reference passed to each call has its own lifetime. Fn(&str) -> String
accepts call arguments with independent lifetimes; the produced String
owns its result.
This complete example calls one borrowed tagger with a short-lived input, then calls it again after that input has been dropped:
fn make_tagger(prefix: &str) -> Box<dyn Fn(&str) -> String + '_> {
Box::new(move |s| format!("{prefix}:{s}"))
}
fn main() {
let prefix = String::from("log");
let tag = make_tagger(&prefix);
let first = {
let input = String::from("temporary");
tag(&input)
};
assert_eq!(first, "log:temporary");
assert_eq!(tag("next"), "log:next");
}
Writing Fn(&'a str) with the captured prefix's lifetime would remove that
independent, any-call-lifetime interface. The starter's MakeTagger alias
uses an outer for<'a> for the factory's prefix lifetime; the elided call
lifetime inside Fn(&str) is a separate part of the type.
Move the capture, then end the borrow
In the supplied closure expression, move makes the closure capture the
prefix variable by value. For the borrowing factory that value is a shared
reference, so the underlying string is still borrowed. For the owning
factory it is a String, whose ownership moves into the closure. move
alone does not turn borrowed data into owned data or make a closure 'static.
In run, finish using and drop the borrowing tagger before moving prefix
into make_static_tagger. A nested scope around the first tagger and its
result collection makes that ordering clear. The result strings can leave
the scope because they own their text.
Read the diagnostic's lifetime relationship
The starter reports that a lifetime may not live long enough. Read which input borrow the compiler is comparing with the return type's requirement. Diagnostic wording and codes depend on the exact program.
A tagger retained after its prefix is dropped is invalid. Similarly,
storing make_tagger(&format!("{}", 7)) for use in a later statement borrows
a temporary that ends too soon. An immediate call within the same statement
can be valid. Bind the prefix to a local variable when the tagger needs it
across statements.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Rust reference solution
Sign in to track your attempts toward unlocking the reference solution.