Why stdlib's Rc<str> and Arc<str> from(String) allocates again?

I was looking at the implementation of From<String> for Rc<str> and it is like this:

#[cfg(not(no_global_oom_handling))]
#[stable(feature = "shared_from_slice", since = "1.21.0")]
impl From<String> for Rc<str> {
    /// Allocate a reference-counted string slice and copy `v` into it.
    ///
    /// # Example
    ///
    /// ```
    /// # use std::rc::Rc;
    /// let original: String = "statue".to_owned();
    /// let shared: Rc<str> = Rc::from(original);
    /// assert_eq!("statue", &shared[..]);
    /// ```
    #[inline]
    fn from(v: String) -> Rc<str> {
        Rc::from(&v[..])
    }
}

The documentation mentions that it allocates a new string slice and copies v into it.

This seems a bit inefficient to me. A new slice is allocated, then the contents of v are copied to it, and then v is destroyed and hence deallocated. Why allocating again when the string already has the data? I think it should be possible to leak a pointer to the string and re-assign it via Rc::from_raw, preventing a new allocation and a copy. What am I missing?

The counters are stored together with the data on the heap, which is the reason you can't reuse the existing allocation I guess, the layout wouldn't match.

2 Likes

Also, String has some unused capacity in the general case which is not representable by str. So, even if the layout issue didn't exist, there'd need to be a call to the allocator to shrink the allocation.

2 Likes

And in general, creating a String usually requires a series of allocations and copies to create more capacity as needed (unless the exact UTF-8 length is known), so this is “just” one more of those.

It'd be nice if there was a way to avoid it, though. Interestingly, impl FromIterator<T> for Rc<[T]> exists (but for Rc<str> doesn’t) and has the same extra allocation cost, even though it could create allocations with space reserved for the reference counts. Perhaps it just hasn’t been considered worth implementing.

1 Like

See also:

3 Likes

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.