13

Looking at the methods available for Vec<T> I stumbled across

into_boxed_slice(self) -> Box<[T]>

String also has such a method (into_boxed_str(self)). The usefulness of having Deref for Vec<T>/String that allows them to be treated like a shared slice (&[T]) is obvious, but I don't see any use for an owned slice (Box<[T]>) except, perhaps, FFI. The Rust GitHub repo only uses into_boxed_slice() in a handful of cases.

Since methods for creating boxed slices are available in std and this container is listed on its main page, I thought that I might be missing something useful about it. What are cases where I should use an owned slice in favor of a Vec<T> or a String?

ljedrz
  • 20,316
  • 4
  • 69
  • 97
  • 4
    Not sure if it's *the* reason, but `Box<[T]>` and `Box` have one less pointer-sized integer as they don't need a capacity — they can't be resized. – Shepmaster Sep 21 '16 at 13:18
  • I thought about this, but it doesn't strike me as a reason enough to have a dedicated container either. – ljedrz Sep 21 '16 at 13:24
  • I'm not sure what you mean by "dedicated container". It's just a combination of existing types - `Box` and `[T]` or `str`. It's not really that different from a `Box`. – Shepmaster Sep 21 '16 at 13:29
  • You're right, though it is listed in the main page of `std`, which gives it a feeling of some nobility :). That's what got me interested in it. – ljedrz Sep 21 '16 at 13:52
  • I'd guess it's probably just meant to be a cheap, safe way of getting the underlying heap pointer. Effectively an "into_inner" method. Probably provided in case somebody really wants to own the underlying buffer for reasons that weren't considered very strongly because it's trivial to implement. – Linear Sep 23 '16 at 00:51
  • Otherwise, to avoid intermediate allocations, you have to go through unsafe shenanigans like `Box::from_raw(&mut vec[..] as *mut [T])`, and then manually forget the Vec, which is just unpleasant. It's nicer to just provide a safe alternative. – Linear Sep 23 '16 at 00:56
  • I saw the implementation and it makes sense; I'd still like to know some practical use of owning the inner buffer, though (if there are other than FFI). – ljedrz Sep 23 '16 at 05:19

2 Answers2

13

The big reason for using into_boxed_slice() is that a boxed slice takes up only as much memory as:

  • The underlying data itself
  • A length field that gives the total length of the data

When using a standard Vec it is possible, and common, for the Vec to obtain more memory than what it actually needs to avoid having to allocate more memory every time a new element is added. That space is essentially unused. You can find out how much extra memory is being used by comparing Vec::len() versus Vec::capacity().

The main place that I have found the into_boxed_slice() function useful is in an in-memory file cache. I load the file into memory using a Vec for simplicity, but once the file is loaded, I no longer need to add or remove elements. So I convert it to a boxed slice using into_boxed_slice(). There would be other ways to achieve the same thing, but in this case the single function call is easier. I like the boxed slice type (as opposed to a Vec) because it clearly signals the intent that the cached file is not meant to modified.

Note: you can actually still use a Vec without the extra overhead by calling Vec::shrink_to_fit(), which will remove the extra allocated elements from the Vec.

Nick Stevens
  • 386
  • 1
  • 3
  • 7
  • 1
    If I can remember correctly, `into_boxed_slice()` calls `shrink_to_fit()` too. It's a valid answer; I was hoping to learn some hidden benefits, but there just might not be any. I'll accept it if nothing surprising comes up soon. – ljedrz Sep 26 '16 at 05:25
  • 1
    `shrink_to_fit` doesn't guarantee that length will equal capacity after it returns. The docs say it will shrink "as much as possible": https://doc.rust-lang.org/std/vec/struct.Vec.html#method.shrink_to_fit `into_boxed_slice` internally does use `into_boxed_slice`, but also guarantees the returned slice will be of length `vec.len()`, so it goes beyond the guarantees of `shrink_to_fit` – Aaron Feb 16 '23 at 17:19
1

I have an application where I have a Vec of tens of thousands of elements, each of which could be a Vec or a Box<[T]> so I save hundreds of thousands of bytes by using into_boxed_slice.

Dave Mason
  • 669
  • 6
  • 15