3 ms·
> So how does Vec return mutable references to elements? Unsafe code, apparently. You can't write your own safe collection class with an iterator and return mut
by herbstein 5y ago
> So how does Vec return mutable references to elements? Unsafe code, apparently. You can't write your own safe collection class with an iterator and return mutable references. You'd need a proof of disjointness system to do that.
If you have a mutable binding to a vector `v` you can get a single mutable reference to one of the elements of that vector at a time. Because getting a single mutable reference to an element requires taking a mutable reference to the vector the disjointedness invariant is upheld. This is the core of the Rust borrow checker. If you're unsure about the semantics you should be reading about it, not trying to sus them out from a fairly surface level discussion on HackerNews.
This code:
for x in &mut v {
*x = // ...
}
is perfectly valid. For every iteration of the loop only a single mutable reference to an element exists, and the reference is dropped before the next mutable reference is taken. This following piece of code is a full example that showcases how taking two mutable references is not allowed.
struct ComplexNonCopyClone {
a: u32,
}
fn main() {
let mut v = vec![
ComplexNonCopyClone { a: 1 },
ComplexNonCopyClone { a: 2 },
ComplexNonCopyClone { a: 3 },
];
let mut_one = v.get_mut(0).unwrap();
let mut_two = v.get_mut(0).unwrap();
mut_two.a = 4;
println!("{}", mut_one.a);
}
Do note that using `mut_one` after `mut_two` is important, as otherwise the compiler will infer that `mut_one` can be dropped before `mut_two` is create, removing the clash.
> So how does Vec return mutable references to elements? Unsafe code, apparently.
The implementation can be found quite easily[0]. It's essentially invariants upheld by runtime checks and knowledge about the state of the given references/pointers. Notice the comments starting with "SAFETY". They explain the assumptions/conditions/reasons that make the code inside the `unsafe` block safe. If these assumptions can never be violated with malicious input or calling patterns then the code is actually safe and it can be a safe wrapper.
[0]: https://doc.rust-lang.org/src/core/slice/index.rs.html#149-190
- Animats 5y agoWe're talking about Vec's iterator, not simple slice access. That "SAFETY" stuff in slice.rs is just a subscript check. Vec's iterator is in [1]. The iterator which returns a mutable reference is unsafe code, because you can't express that concept in safe code. To recap: the interesting issue is the thread pool example which spins off a new thread with a mutable reference to each element of an array. That's a rather unusual thing to do. It is only safe if each iteration returns a reference disjoint from all other references. Because Rust at the language level does not understand disjointness within an array, it needs a hack using "unsafe" to make this work. The borrow checker would not allow this in safe code. chunks_mut uses the same trick, with more unsafe code. My point in all this is that because the language doesn't have syntax for talking about disjointness of parts of an array, each time this comes up, another hack in unsafe code is required. But enough here; I may say more on the Rust forums. [1] https://doc.rust-lang.org/src/core/slice/iter.rs.html https://doc.rust-lang.org/src/core/slice/iter.rs.html