Why is Box &str not owned like String is?

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:

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=fcf3be7b76b4bf4b6d35e903070176cd

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> (without &) owns the string data.

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 }.

8 Likes

@kornel, wow thank you that's so obvious now that you clear things up.

But I've never before seen a plain str before, can they be made easily?

For instance just doing Box::new(*my_ampersand_str) results in:

the size for values of type str cannot be known at compilation time
...
note: required by Box::<T>::new

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.)

2 Likes

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]>.

1 Like

Ah, right! String::into_boxed_str is another option of converting String to Box<str> (equivalent to using the From implementation I mentioned above).

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.