3

Why does Rust does not allow creating a slice sa as a value {ptr, len} on the stack and only allows to create rsa as a references to some (anonymous?) value called a fat pointer?

// a is five i32 values on the stack
let a = [1, 2, 3, 4, 5];

// rsa is a reference to (anonymous?) {ptr,len} on the stack
let rsa = &a[..];

// sa would be an explicit struct {ptr,len} on the stack
let sa = a[..];
error[E0277]: the size for values of type `[{integer}]` cannot be known at compilation time
 --> src/main.rs:9:9
  |
9 |     let sa = a[..];
  |         ^^   ----- help: consider borrowing here: `&a[..]`
  |         |
  |         doesn't have a size known at compile-time
  |
  = help: the trait `std::marker::Sized` is not implemented for `[{integer}]`
  = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
  = note: all local variables must have a statically known size
  = help: unsized locals are gated as an unstable feature
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Andrey.Kozyrev
  • 579
  • 8
  • 17
  • 2
    This is just how the language is currently defined. There is no theoretical obstacle to supporting dynamically sized types on the stack, but so far this hasn't been implemented. There is an [accepted RFC to support this at some point in the future](https://github.com/rust-lang/rfcs/blob/master/text/1909-unsized-rvalues.md), though. – Sven Marnach Sep 08 '20 at 12:33
  • That's exactly what I'm missing: what is dynamically sized here? It is a fixed struct with two usize values {ptr, len}. Why only expose it as a reference? – Andrey.Kozyrev Sep 08 '20 at 12:37
  • @Andrey.Kozyrev: whilst the `usize` that holds `len` is on the stack, its value is not known at compile-time (you can read that code and reason that in this specific case it must be 5, but the compiler doesn't have a general way of doing that). Consider the situation where `a` is not in the local frame, but instead `rsa` is passed in as a function parameter: how then could the compiler determine `len` in order to allocate sufficient space in the local frame for `sa`? – eggyal Sep 08 '20 at 12:40
  • `[T]` is dynamically sized because it doesn't have any size known at compile-time, there can be any number of items in there all stored on the stack. `&[]` is exposed as a fat pointer because... it's *is* a non-owning pointer? What would doing otherwise give you? – Masklinn Sep 08 '20 at 12:41
  • @eggyal - Vec is {ptr, cap, len} and compiler does not know the values of both cap and len. It does not stop Vec to be a legit value. However slice as a value is forbidden. – Andrey.Kozyrev Sep 08 '20 at 12:46
  • @Andrey.Kozyrev: But the actual data behind the `Vec` is stored on the heap. Holding a `Vec` on the stack is analogous to the fat pointer `rsa`. – eggyal Sep 08 '20 at 12:48
  • @Masklinn - I have no idea what it would give me, I'm trying to understand Rust and my C++ background wants to know how it is laid out in memory and why ) – Andrey.Kozyrev Sep 08 '20 at 12:49
  • 2
    Looking again at how you've written the question, I think your misunderstanding is in what `rsa` is: it is exactly what you expect `sa` to be, a pointer and a length held on the stack. – eggyal Sep 08 '20 at 12:52
  • @eggyal: if I try to grab a slice of a vector with actual data on the heap - I will get the same error. My only guess is that if a slice is a value it can be moved somewhere and outlive the data it refers to. That's why compiler creates an anonymous {ptr,len} and only allows a reference to it in the current stack-frame. – Andrey.Kozyrev Sep 08 '20 at 12:55
  • 7
    The reference is not *to* `{ptr, len}`—the reference **is** a `{ptr, len}` *to the underlying data*. – eggyal Sep 08 '20 at 12:56
  • 1
    What @eggyal said - this is important. Try [printing the size](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=d7e0e0613efbbe284d7495ca20195717) of `rsa` and you'll see it's exactly two machine words. – user4815162342 Sep 08 '20 at 13:02
  • @Andrey.Kozyrev `&[]` *itself* is the combination of a pointer and a length. It's exactly what you're looking for. That's why you can create a slice [from_raw_parts](https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html) by just passing it a pointer and a length. And all it does is [pack them together](https://doc.rust-lang.org/src/core/ptr/mod.rs.html#264-269). `[T]` is the buffer that's behind the pointer, and the only thing the compiler knows about that is that it holds Ts but not how many (or that'd be an array `[T;N]`) – Masklinn Sep 08 '20 at 13:06
  • 1
    Also SO is absolutely awful for this sort of conversations. Discourse or reddit might be a better venue. And IIRC [the slices chapter of the book](https://doc.rust-lang.org/book/ch04-03-slices.html) covers their structure rather well. I think there's also a "rust for C++ programmers" guide somewhere but I don't know whether it covers this topic. – Masklinn Sep 08 '20 at 13:11

0 Answers0