2

I want to know the difference between a slice ([T]) and a reference to a slice (&[T]). I do not get how slices are unsized types, can't the compiler deduce the size of a slice through the source code?

I understand we need a reference to a slice to make it useful however, this would only make sense If I understand how slices are unsized.

Moreover, I wanted to know the difference between str and &str. I know one is a slice and the other is a reference to a slice and that &str stores a reference to the start of the string and the length as well. But it confuses me as to why we can't use str alone without the reference. Why can't we store the size within the str slice itself. Why do we need a reference to it. I come from a c++ background so maybe using c++ types and terminologies can clear things up!

Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77

1 Answers1

5

The reference, &[T] or &str is just a pointer (and a length). Where does this pointer point at? It points at some data. This data is the [T] (or str), the slice itself, the actual bytes of the data. There is no correspondance to that in C++.

Since the data can be of any length, there is no much we can do with [T]. We cannot take it as a parameter, we cannot return it, it always has to be wrapped with some kind of reference. Usually it's & or &mut, but it can also be Box or Rc or Arc, for owned slices.

Sometimes, the compiler could infer the slice size (e.g. when we're slicing via a closed constant range). But a slice type never encodes the length. Only a reference to it does, at runtime. This is because it is useful to have some data that can be of any runtime-determined length, like C++'s std::span or std::string_view.

Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77
  • 1
    How is a slice type itself represented in memory. I know how the reference to a slice is represented in memory but how is the slice itself represented. Could you also explain how a `owned` slice is referenced in memory and what benefits a owned slice provides. – Humble Penguin Jun 19 '23 at 14:39
  • 1
    @HumblePenguin A slice is just contiguous bytes interpreted as repeat of a type. It's like an array but of unknown length. And the benefit of an owned array is not having a lifetime, so it can be freely passed around and saved for later, like with any owned type (`Box` vs `&mut T`). – Chayim Friedman Jun 19 '23 at 14:44
  • I would rephrase that last comment saying that in memory a slice is just identical to an array of the same length. The difference between a slice and an array is that for the latter the length is part of its type; while for the former, the length is erased from the type and unknown to the compiler. – rodrigo Jun 19 '23 at 16:34
  • shoudn't this line of code: `let slic = arr[..3];` create a copy of the array `arr` from the first element up till the second one. Instead I get a error telling me that it is unsized. – Humble Penguin Jun 19 '23 at 18:24
  • @HumblePenguin No, because indexing into array creates a slice, even if the index is a constant closed range. – Chayim Friedman Jun 19 '23 at 18:26
  • Why do I need the borrow symbol to create a slice here? I mean I know slices are unsized types however we can clearly see the size here. And what does `Box<[T]>` actually mean? Am I storing a dynamically sized array of type T on the heap or am I storing a slice? – Humble Penguin Jun 19 '23 at 18:34
  • @HumblePenguin But this is the point: indexing using a range _always_ produces a slice, even when the size could be known at compile time, because this is how it is defined (and because indexing to array only when the size is known at compile-time will be confusing and hard to do). – Chayim Friedman Jun 19 '23 at 18:37
  • @HumblePenguin And for `Box<[T]>`, you can look on it as dynamically-sized non-growable array on the heap, similar to `Box<[T; N]>` but dynamically sized, or similar to `Vec` but that cannot grow. Or you can look on it as the owned version of `&[T]`. By Rust terminology, you're storing a slice because this is the type you have, but you can call it an array if that works better for you. – Chayim Friedman Jun 19 '23 at 18:39
  • `arr[1..3]` return a slice. However as you said slices have a unknown size at compile time and can only be used through a pointer. My question is how does the slice it self look like? The pointer that points to it has a memory location to the beginning of the slice and as well as the length. However let's assume we did not need a pointer to access a unsized slice and that we could access a slice directly using it's bare type. How would the slice be stored in memory then? Would it be a copy of the array of still refer to the original array – Humble Penguin Jun 19 '23 at 18:49
  • @HumblePenguin It will be one `T`, then immediately after it another `T`, and this is all part of `arr` - that is, their memory is overlapping (the slice is the second and third elements in the array, and they're both in the slice and in the array). – Chayim Friedman Jun 19 '23 at 18:54
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/254157/discussion-between-humble-penguin-and-chayim-friedman). – Humble Penguin Jun 20 '23 at 06:14