As discussed in this question, I need str data on the heap so that Ob can own it, but I prefer Box instead of changing Ob.id type to String (for it's smaller memory footprint). This is demonstrated here:
Which I know won't work because of:
returns a value referencing data owned by the current function
If I were to instead change Ob.id to a String type and get rid of all lifetimes, this code of course will compile.
But why doesn't Box<&'a str> ensure the id is placed on the heap for Ob to then own it?
Is this another advantage of using String over Box<&str>? Or is there a way to make this still compile?
Box<&str> owns only a copy of the reference to string data that neither of them owns.
The advantage of String over Box<str> is only in tracking capacity, which allows it to grow efficiently. It's basically something like struct String { data: Box<str>, used_length: usize }.
You can convert String into Box<str>, but also &str to Box<str>, using the From/Into trait. Something like Box::<str>::from(my_ampersand_str) should work; or my_ampersand_str.into() if type inference can figure out that Box<str> is what you want. Conversion from String to Box<str> should be cheap (i.e. basically free) if the String is exactly full to capacity, otherwise it involves copying all the data, similar to calling String::shrink_to_fit.
(Feel free to study all the From impls involving Box (starting here); note that e.g. Box<…, Global> stands for just Box<…> but rustdoc can’t help but render the unstable extra optional allocator arguments.)
Incidentally, under the hood a str is a (plain/bare) [u8] slice, another dynamically sized type (DST). The fact that they are dynamically sized is why you generally see them behind pointers or in Boxes, et cetera -- having DSTs on the stack is not (currently) supported in Rust. Going from String into Box<str> is like going from Vec<T> into Box<[T]>.