How does Vec's iterator return a mutable reference?

Incidentally, what's the goal? Some syntax could be introduced to perform replace without unsafe appearing in the observable source code (ala slice patterns and split_mut), to push the trust boundary from the core library into the language/compiler itself, I suppose.

1 Like

Where this all came from is that I was looking at ways to get separate threads working on each element of a vector. This is not possible without unsafe code at some level. Various workarounds to do this were suggested. Underneath, they always needed unsafe code.

Different unsafe code for each approach. Too many workarounds. One hopes they are safe, but it's hard to be sure.

"Take" seems to be the most general sound approach. That's just move semantics meets "Option".
You can't, currently, do a move out of an option at the language level. The compiler already knows about "Option", for optimization purposes, so it's built into the language already.

I'd argue that a good language change would be to allow moves out of mutable Option items using "=". "Take" at the language level. Is that 1) sound, and 2) sufficient to cover all the use cases above?

I've suggested before that languages with move semantics should have a replace operator instead of an assignment operator, though I think it's rather late to add such a thing to the existing Rust language.

1 Like

There's value in having standard lib types implemented in plain-ol-Rust. If nothing else as a reference, but also it keeps the standard lib from becoming "magical" and doing things normal Rust programmers can't do. Redefining = to act differently for Option would be such a magical feature. Also I don't think further complicating the behavior of = is a good idea or likely to happen.

Making core::mem::replace or swap some sort of intrinsic operator would be possible, though. I was going to sketch it out, but basically see @mbrubeck's post on <-. That would move the trust boundary into the language; replace would just use the operator.

take semantics could also be a trait, automatically implemented for T: Default. This could conceivably back an overloadable operator, too. Then you could have the behavior you wish for on Option, but not magically for just Option.

However without the intrinsic above, they'd still be using the unsafe keyword via replace.


In the case of unsafe in the core/standard library, you're trusting pretty much the same people to get the analysis of Rust's guarantees right as when it's in the language/rustc itself. I guess there probably is some additional scrutiny the the language level, but the body of replace is about as simple as unsafe can get. I suppose it doesn't bother me because you must trust the compiler (library and rustc) as a whole already anyway.

That said, I think it makes sense to minimize unsafe in the core/standard library itself wherever feasible too, and if the <- operator came to be, replace would be such a case. Basically <- would become part of the 'base safety model" you implicitly trust and so don't have to think about, the body of replace would move to rustc to back the <- operator, and replace would get rewritten to use <-.

Good points. I saw that, too, and marked up my suggestion to cross-out doing "take" with "=".

What's really going on here is that all those simple primitive functions (swap, replace, take) temporarily break Rust's single-ownership invariants, but put them back together before returning. Those aren't so bad, because they're two-line functions that only have the invariant broken between two statements.

Now, crossbeam::scoped requires much more elaborate reasoning to insure soundness. The examples seem sound, but whether there's some way to violate the one mutable reference rule that way isn't entirely clear. A previous implementation turned out to have a hole. Also see scoped::threadpool, which does roughly the same thing but with different unsafe code.

At some point, there's too much unsafety.

1 Like

Wouldn't a swap be better? You can write replace in terms of swap, but not the opposite.

swap as a primitive makes a lot of sense. It inherently preserves single-type properties, and, as pointed out, you can write replace in terms of swap.

Could you do crossbeam::scoped with swap?

In the link you quoted, I suggested both operators! But I think a replace operator would be more widely used in practice, since it is a straightforward replacement for both the = operator and core::mem::replace (which in my own Rust code is significantly more common than core::mem::swap).

With the right semantics for the replace operator, you could even use in place of the swap operator in some cases:

a <- b <- a

This is similar to this way of swapping two variables with replace plus assignment:

let mut a = vec![1];
let mut b = vec![2];

a = replace(&mut b, a);

(Playground)

(This doesn't fully replace swap because it requires ownership of a, while swap only requires a mutable borrow.)

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.