Is it possible to have a non-borrowed slice? - rust

If I try this:
let vector = vec![1, 2, 3];
let slice = vector[1..2];
I get a compiler error:
error[E0277]: the trait bound `[{integer}]: std::marker::Sized` is not satisfied
--> src/main.rs:3:9
|
3 | let slice = vector[1..2];
| ^^^^^ ------------ help: consider borrowing here: `&vector[1..2]`
| |
| `[{integer}]` does not have a constant size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `[{integer}]`
= note: all local variables must have a statically known size
I get that we need let slice = &vector[1..2] as the compiler kindly suggests. This makes sense: a slice always comes from another value, thus you need to borrow the vector in this example.
However, I tried this:
let vector = vec![1, 2, 3];
let borrowed_vector = &vector;
let slice = borrowed_vector[1..2];
and I am back to the same error.
I ask because it seems a bit weird to require the &vector[] syntax if in fact borrowing (&) is always required.

A non-borrowed slice ([T]) exists, but you cannot use it as value as-is. It is only useful in other types where it is behind a pointer of some kind. For example (not exhaustive):
In borrowed slices: &[T]
In boxed slices (i.e. owned slices): Box<[T]>
In ref-counted slices: Rc<[T]>
That is because the compiler cannot know what the size of an unsized type on the stack but a pointer has a known size.
The type can also be used as part of a trait bound for static polymorphism: U: AsRef<[T]>.
It seems a bit weird to require this syntax &vector[] if in fact borrowing (&) is always required.
It is not always required to use an & with the indexing syntax because sometimes the referenced value can be dereferenced:
let scores = vec![1, 2, 3];
let score = scores[0];
See also:
What is the return type of the indexing operation?

Related

Rustlings slice primitives

One of the rustlings exercises on primitive is about slices.
When I tried to solve this exercise I started by using the [start...end] syntax to take a slice into the given array as such
fn slice_out_of_array() {
let a: [u8; 5] = [1, 2, 3, 4, 5];
let nice_slice = a[1..4];
assert_eq!([2, 3, 4], nice_slice)
}
The compiler complains and tells me:
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> exercises/primitive_types/primitive_types4.rs:10:9
|
10 | let nice_slice = a[1..4];
| ^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `[u8]`
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
help: consider borrowing here
|
10 | let nice_slice = &a[1..4];
| +
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.
running rustc --explain E0277 doesn't really answer my question. It explains what it means for an argument not to implement a trait when the function specifies it should. That is clear to me, but i am not completely clear on what is going wrong here.
This is what i think is happening, but I'd like to hear other's opinions.
The compiler can't figure out from the slice syntax how big the resulting slice will be and so it can't allocate the proper space on the stack.
By default slices are primitives and live on the stack instead of the heap.
If I take the suggestion and add & the compiler pushes on the stack a reference -- which has a known size -- to the original array already on the stack. This solves the problem.
Is this correct? Is there a case where i can take a slice and not add the & symbol?
The compiler can't figure out from the slice syntax how big the resulting slice will be and so it can't allocate the proper space on the stack.
This is true. We say that slices are Dynamically Sized Types (DSTs), or that they are not Sized, or that they do not implement the Sized trait (this is the reason rustc gives you E0277, type does not implement trait).
By default slices are primitives and live on the stack instead of the heap.
This is not precise: slices are primitives, but Rust primitives, like any other object, can also live on the heap. It is just that you are trying to move it, so the compiler needs to reserve space for it on the stack.
If I take the suggestion and add & the compiler pushes on the stack a reference -- which has a known size -- to the original array already on the stack. This solves the problem.
Yes. Essentially, the compiler just offsets the address and shorten the length. A reference to slice is a pair of (address, length).
Is there a case where i can take a slice and not add the & symbol?
Yes and no.
No, because you always take a reference of some kind. All DSTs can only come behind an indirection (a reference, a Box, a Rc...).
But also yes, because you does not always need to add the & symbol. Sometimes the compiler is doing it for you: when you call a method on the slice, thanks to autoref:
fn slice_out_of_array() {
let a: [u8; 5] = [1, 2, 3, 4, 5];
a[1..4].is_empty(); // Just an example. Equivalent to:
(&a[1..4]).is_empty();
}
You can also moves it to the heap (but this also takes a reference under the hood):
fn slice_out_of_array() {
let a: [u8; 5] = [1, 2, 3, 4, 5];
let b: Box<[u8]> = a[1..4].into();
}

Why is Rust telling "unknown size at compile time" instead of another error in a (invalid) slice to slice assignment?

I have a weird looking piece of code and I understand that Rust compiler rejects it, but I don't understand the specific error message.
TL;DR; Why does Rust rejects this with "doesn't have a size known at compile-time" instead of something like "illegal syntax" or "can't assign a slice to a slice"?
fn main() {
let mut data1 = vec![0, 1, 2, 3].as_slice();
let mut data2 = vec![8, 9].as_slice();
data1[1..3] = *data2; // of course this is illegal; but I don't understand the error message
}
This is the code. In theory it should replace a sub slice of data1 with the data in slice data2. (The proper way would be a for loop for example, I know!). But let's have a look at this. Rust compiler says:
error[E0277]: the size for values of type `[{integer}]` cannot be known at compilation time
--> src\main.rs:4:5
|
4 | data1[1..3] = *data2;
| ^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `[{integer}]`
Why is the error at data1[1..3], only on the left hand side of the assignment? I expected that Rust compiler tells the error is on the right side of the assignment or even the whole assigment. Something like "can't assign a slice to a slice".
But why is Rust telling exactly this message? Why is data1[1..3] of unknown size in this case? Of course [{integer}] is not Sized. But there should be no stack allocation necessary at this point? I'd expect any other error message.
I can't see a slice on the left hand side of your assignment and neither can the compiler!
Always try to reduce your example as much as possible, most of the time doing so you would find what the compiler is actually complaining about. So, if you would try and write this:
let data1 = [0u8, 1, 2, 3];
let x = data1[1..3];
You would see, what the compiler is actually complaining about in your example:
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> src/main.rs:4:9
|
4 | let x = data1[1..3];
| ^ ----------- help: consider borrowing here: `&data1[1..3]`
| |
| doesn't have a size known at compile-time
You see, there's a huge difference between [T] and &[T]! [T] is a contiguous sequence of Ts, while &[T] is a dynamically-sized view into this contiguous sequence. The former has no statically known size while the latter does.
And before you say that you used the Vec::as_slice method, after that you tried to take a slice of a slice, that is:
// Type of `data1` is `&[u8]`
let data1 = vec![0u8, 1, 2, 3].as_slice();
// Type of `x` is `[u8]`
// (which doesn't have a size known at compile-time
let x = data1[1..3];
So I believe the answer to your question is that the compiler didn't get to the point where it can actually look at the other side of the assignment, because while it tried to figure out the left hand side it already found a problem: an expression that has no known size at compile time.
Now, if you would actually write a slice on the left hand side:
let mut data1 = [0u8, 1, 2, 3];
let data2 = [8u8, 9];
&mut data1[1..3] = &data2[..];
Then the compiler would complain about the invalid nature of the left hand side (among other things):
error[E0070]: invalid left-hand side of assignment
--> src/main.rs:6:22
|
6 | &mut data1[1..3] = &data2[..];
| ---------------- ^
| |
| cannot assign to this expression

Borrowing error when pushing reference into vector that is on the same scope [duplicate]

For reasons related to code organization, I need the compiler to accept the following (simplified) code:
fn f() {
let mut vec = Vec::new();
let a = 0;
vec.push(&a);
let b = 0;
vec.push(&b);
// Use `vec`
}
The compiler complains
error: `a` does not live long enough
--> src/main.rs:8:1
|
4 | vec.push(&a);
| - borrow occurs here
...
8 | }
| ^ `a` dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are created
error: `b` does not live long enough
--> src/main.rs:8:1
|
6 | vec.push(&b);
| - borrow occurs here
7 | // Use `vec`
8 | }
| ^ `b` dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are created
However, I'm having a hard time convincing the compiler to drop the vector before the variables it references. vec.clear() doesn't work, and neither does drop(vec). mem::transmute() doesn't work either (to force vec to be 'static).
The only solution I found was to transmute the reference into &'static _. Is there any other way? Is it even possible to compile this in safe Rust?
Is it even possible to compile this in safe Rust?
No. What you are trying to do is inherently unsafe in the general case.
The collection contains a reference to a variable that will be dropped before the collection itself is dropped. This means that the destructor of the collection has access to references that are no longer valid. The destructor could choose to dereference one of those values, breaking Rust's memory safety guarantees.
note: values in a scope are dropped in the opposite order they are created
As the compiler tells you, you need to reorder your code. You didn't actually say what the limitations are for "reasons related to code organization", but the straight fix is:
fn f() {
let a = 0;
let b = 0;
let mut vec = Vec::new();
vec.push(&a);
vec.push(&b);
}
A less obvious one is:
fn f() {
let a;
let b;
let mut vec = Vec::new();
a = 0;
vec.push(&a);
b = 0;
vec.push(&b);
}
That all being said, once non-lexical lifetimes are enabled, your original code will work! The borrow checker becomes more granular about how long a value needs to live.
But wait; I just said that the collection might access invalid memory if a value inside it is dropped before the collection, and now the compiler is allowing that to happen? What gives?
This is because the standard library pulls a sneaky trick on us. Collections like Vec or HashSet guarantee that they do not access their generic parameters in the destructor. They communicate this to the compiler using the unstable #[may_dangle] feature.
See also:
Moved variable still borrowing after calling `drop`?
"cannot move out of variable because it is borrowed" when rotating variables

How do I add references to a container when the borrowed values are created after the container?

For reasons related to code organization, I need the compiler to accept the following (simplified) code:
fn f() {
let mut vec = Vec::new();
let a = 0;
vec.push(&a);
let b = 0;
vec.push(&b);
// Use `vec`
}
The compiler complains
error: `a` does not live long enough
--> src/main.rs:8:1
|
4 | vec.push(&a);
| - borrow occurs here
...
8 | }
| ^ `a` dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are created
error: `b` does not live long enough
--> src/main.rs:8:1
|
6 | vec.push(&b);
| - borrow occurs here
7 | // Use `vec`
8 | }
| ^ `b` dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are created
However, I'm having a hard time convincing the compiler to drop the vector before the variables it references. vec.clear() doesn't work, and neither does drop(vec). mem::transmute() doesn't work either (to force vec to be 'static).
The only solution I found was to transmute the reference into &'static _. Is there any other way? Is it even possible to compile this in safe Rust?
Is it even possible to compile this in safe Rust?
No. What you are trying to do is inherently unsafe in the general case.
The collection contains a reference to a variable that will be dropped before the collection itself is dropped. This means that the destructor of the collection has access to references that are no longer valid. The destructor could choose to dereference one of those values, breaking Rust's memory safety guarantees.
note: values in a scope are dropped in the opposite order they are created
As the compiler tells you, you need to reorder your code. You didn't actually say what the limitations are for "reasons related to code organization", but the straight fix is:
fn f() {
let a = 0;
let b = 0;
let mut vec = Vec::new();
vec.push(&a);
vec.push(&b);
}
A less obvious one is:
fn f() {
let a;
let b;
let mut vec = Vec::new();
a = 0;
vec.push(&a);
b = 0;
vec.push(&b);
}
That all being said, once non-lexical lifetimes are enabled, your original code will work! The borrow checker becomes more granular about how long a value needs to live.
But wait; I just said that the collection might access invalid memory if a value inside it is dropped before the collection, and now the compiler is allowing that to happen? What gives?
This is because the standard library pulls a sneaky trick on us. Collections like Vec or HashSet guarantee that they do not access their generic parameters in the destructor. They communicate this to the compiler using the unstable #[may_dangle] feature.
See also:
Moved variable still borrowing after calling `drop`?
"cannot move out of variable because it is borrowed" when rotating variables

Why does the compiler tell me to consider using a `let` binding" when I already am?

What is my error and how to fix it?
fn get_m() -> Vec<i8> {
vec![1, 2, 3]
}
fn main() {
let mut vals = get_m().iter().peekable();
println!("Saw a {:?}", vals.peek());
}
(playground)
The compiler's error suggests "consider using a let binding" — but I already am:
error[E0597]: borrowed value does not live long enough
--> src/main.rs:6:45
|
6 | let mut vals = get_m().iter().peekable();
| ------- ^ temporary value dropped here while still borrowed
| |
| temporary value created here
7 | println!("Saw a {:?}", vals.peek());
8 | }
| - temporary value needs to live until here
|
= note: consider using a `let` binding to increase its lifetime
This is obviously a newbie question -- though I thought I'd written enough Rust at this point that I had a handle on the borrow checker... apparently I haven't.
This question is similar to Using a `let` binding to increase value lifetime, but doesn't involve breaking down an expression into multiple statements, so I don't think the problem is identical.
The problem is that the Peekable iterator lives to the end of the function, but it holds a reference to the vector returned by get_m, which only lasts as long as the statement containing that call.
There are actually a lot of things going on here, so let's take it step by step:
get_m allocates and returns a vector, of type Vec<i8>.
We make the call .iter(). Surprisingly, Vec<i8> has no iter method, nor does it implement any trait that has one. So there are three sub-steps here:
Any method call checks whether its self value implements the Deref trait, and applies it if necessary. Vec<i8> does implement Deref, so we implicitly call its deref method. However, deref takes its self argument by reference, which means that get_m() is now an rvalue appearing in an lvalue context. In this situation, Rust creates a temporary to hold the value, and passes a reference to that. (Keep an eye on this temporary!)
We call deref, yielding a slice of type &[i8] borrowing the vector's elements.
This slice implements the SliceExt trait, which does have an iter method. Finally! This iter also takes its self argument by reference, and returns a std::slice::Iter holding a reference to the slice.
We make the call .peekable(). As before, std::slice::Iter has no peekable method, but it does implement Iterator; IteratorExt is implemented for every Iterator; and IteratorExt does have a peekable method. This takes its self by value, so the Iter is consumed, and we get a std::iter::Peekable back in return, again holding a reference to the slice.
This Peekable is then bound to the variable vals, which lives to the end of the function.
The temporary holding the original Vec<i8>, to whose elements the Peekable refers, now dies. Oops. This is the borrowed value not living long enough.
But the temporary dies there only because that's the rule for temporaries. If we give it a name, then it lasts as long as its name is in scope:
let vec = get_m();
let mut peekable = vec.iter().peekable();
println!("Saw a {:?}", vals.peek());
I think that's the story. What still confuses me, though, is why that temporary doesn't live longer, even without a name. The Rust reference says, "A temporary's lifetime equals the largest lifetime of any reference that points to it." But that's clearly not the case here.
This is happening because you are trying to run your .iter().peekable() on the actual vector inside of get_m(), which is getting re-referenced by vals.
Basically, you want something like this:
fn get_m() -> Vec<i8> {
vec![1, 2, 3]
}
fn main() {
let vals = get_m();
let mut val = vals.iter().peekable();
println!("Saw a {:?}", val.peek());
}
(Playground)
Result:
Saw a Some(1)

Resources