Whats the mean of &mut *smth?

I think, when I make a ref to dereferenced object, I do nothing. But I have a code:


 fn push_f32(dst: &mut Vec<f32>, value: f32, eps: f32) {
                            for v in &mut *dst { // <----- here!!!!
                                if (*v - value).abs() < eps {
                                    return;
                                }
                            }

                            dst.push(value);
                        }

If I make just for v in dst { in second line, I see this error:


   Compiling axis_drawer v0.1.0 (D:\RustProj\axis_drawer)
error[E0382]: borrow of moved value: `dst`
   --> src\solver.rs:376:29
    |
369 |                         fn push_f32(dst: &mut Vec<f32>, value: f32, eps: f32) {
    |                                     --- move occurs because `dst` has type `&mut Vec<f32>`, which does not implement the `Copy` trait
370 |                             for v in dst {
    |                                      --- `dst` moved due to this implicit call to `.into_iter()`
...
376 |                             dst.push(value);
    |                             ^^^^^^^^^^^^^^^ value borrowed here after move
    |
note: `into_iter` takes ownership of the receiver `self`, which moves `dst`
   --> C:\Users\user\.rustup\toolchains\stable-i686-pc-windows-gnu\lib/rustlib/src/rust\library\core\src\iter\traits\collect.rs:262:18
    |
262 |     fn into_iter(self) -> Self::IntoIter;
    |                  ^^^^
help: consider creating a fresh reborrow of `dst` here
    |
370 |                             for v in &mut *dst {
    |                                      ++++++

It looks inconsistent

As the compiler calls out, this operation is called a “reborrow”. It doesn’t do anything to the pointer at run-time, but in the context of Rust’s ownership and borrowing system, re-borrowing a mutable reference’s target, such as &mut *dst here, is (very much like)[1] borrowing dst whereas the default access to the value dst directly would be moving it.

This is not made any less confusing by the fact that in many cases, but not in this one, mutable references will be re-borrowed implicitly – instead of being moved.

By the way, I think this question would have been a better fit on users.rust-lang.org, rather than the internals forum.

On second thought, maybe with the motivation of “should/could we change the language here”, it can be fitting for the internals forum, too. I personally think it’d be a good idea to make code like this also create an implicit re-borrow, in case we can come up with a principled and backwards-compatible approach to implement this.


  1. there are subtle differences since only dst’s target is borrowed, so this reference can live a bit longer when the variable dst might have already gone out of scope but the target is still live ↩︎

5 Likes

If we "desugar" the for loop, it becomes a bit easier to see why dst is consumed instead of borrowed here:

fn push_f32(dst: &mut Vec<f32>, value: f32, eps: f32) {
    {
        let mut _iter = IntoIterator::into_iter(dst);
        while let Some(v) = _iter.next() {
            if (*v - value).abs() < eps {
                return;
            }
        }
    }

    dst.push(value);
}
error[E0382]: borrow of moved value: `dst`
  --> src/lib.rs:11:5
   |
1  | fn push_f32(dst: &mut Vec<f32>, value: f32, eps: f32) {
   |             --- move occurs because `dst` has type `&mut Vec<f32>`, which does not implement the `Copy` trait
2  |     {
3  |         let mut _iter = IntoIterator::into_iter(dst);
   |                         ---------------------------- `dst` moved due to this method call
...
11 |     dst.push(value);
   |     ^^^ value borrowed here after move
   |
note: `into_iter` takes ownership of the receiver `self`, which moves `dst`
  --> /rustc/6b771f6b5a6c8b03b6322a9c77ac77cb346148f0/library/core/src/iter/traits/collect.rs:268:18

There's no reborrow performed because there's no autoderef done to generic parameters; either the provided type satisfies the bounds or it doesn't.

But still, the MIR for reborrow or not is almost exactly the same: [playground]

-fn iter_mut(_1: &mut Vec<f32>) -> () {
+fn iter_move(_1: &mut Vec<f32>) -> () {
    debug ns => _1;
    let mut _0: ();
    let mut _2: std::slice::IterMut<'_, f32>;
    let mut _3: std::slice::IterMut<'_, f32>;
    let mut _4: std::option::Option<&mut f32>;
    let mut _5: &mut std::slice::IterMut<'_, f32>;
    let mut _6: isize;
    let _8: ();
    scope 1 {
        debug iter => _3;
        let _7: &mut f32;
        scope 2 {
            debug n => _7;
        }
    }

    bb0: {
-       _2 = <&mut Vec<f32> as IntoIterator>::into_iter(_1) -> [return: bb1, unwind continue];
+       _2 = <&mut Vec<f32> as IntoIterator>::into_iter(move _1) -> [return: bb1, unwind continue];
    }

    bb1: {
        _3 = move _2;
        goto -> bb2;
    }

    bb2: {
        _5 = &mut _3;
        _4 = <std::slice::IterMut<'_, f32> as Iterator>::next(_5) -> [return: bb3, unwind continue];
    }

    bb3: {
        _6 = discriminant(_4);
        switchInt(move _6) -> [0: bb6, 1: bb4, otherwise: bb5];
    }

    bb4: {
        _7 = move ((_4 as Some).0: &mut f32);
        _8 = touch_f32(_7) -> [return: bb2, unwind continue];
    }

    bb5: {
        unreachable;
    }

    bb6: {
        return;
    }
}

There's two ways we could make this compile: either we could have for syntax special case references to insert a reborrow in the "desugar," or we could separate reborrowing from autoderef and always reborrow when passing a reference as a function parameter, even when generic.

There might've been a difference in behavior with the OG borrow checker, but with NLL I'm pretty sure that adding the reborrow is always no functional change. The most it might do is lead to the reference being live and present in an async witness table when it wouldn't've been prior.

Last time I checked I think there still are code examples where moving a mut reference compiles fine, and that break with a re-borrow. I've never come around to try collecting a list of such cases though, and I don't know if it's ever problematic e. g. if the moved value would have been passed to a for loop like here, or if the examples involved other things like variable assignments and re-assignments.

Why do we use into_iter instead of iter_mut here?
I thought, for el in &mut v is a sugar over iter_mut.
Why compiler selected borrowing here?

It’s easy to come to this conclusion, however it’s only partially correct. If you have - say - a Vec<T> in a variable x, then calling x.into_iter() will always call you the Vec<T>: IntoIterator<Item = T> implementation, but there’s two more, &'a mut Vec<T>: IntoIterator<Item = &'a mut T> for once, and the other, &'a Vec<T>: IntoIterator<Item = &'a T>.

These implementations use the same code and produce the same iterator as inherent methods .iter_mut() and .iter() methods of Vec<T> (or slices, [T], more specifically), in fact the relevant trait impl is implemented by simply calling .iter_mut() (or .iter() for the shared reference one) directly.

Why it works this way? Less compiler magic! There’s a single desugaring, and the existing trait solver as a mechanism to choose the right implementation for the use-case. Traits are the go-to solution for all kinds of syntactical overloading based on types in Rust, so we use them whenever they work fine. Hence Rust compiler does nothing directly for for loops to choose between into_iter/iter_mut/iter directly, but this choice is left to be done by the trait system, and the implementations of the IntoIterator trait.

6 Likes

As far as I can intuit, there'd need to be invariant lifetimes involved and entangled with the lifetime of the borrow itself. With that it's straightforward to construct an example where for x: &mut _, call(x) works and call(&mut x) doesn't, but call(&mut *x) still works.

I wouldn't be too surprised to see there's a more involved case which confuses the compiler enough to make a reborrow not compile. But, at the least, that'd be a bug; you should always be able to reborrow a reference and get one with an identical lifetime.

User implemented derefs can't be reborrowed independently of deref coercion, though, of course.

Hmm… either I’m misremembering, or I mixed up something else for a re-borrow at the time, or the compiler has changed, or I’m simply not able to come up with an example anymore :sweat_smile:

I can't understand the purpose of this implementation. If we have a mutable reference to object, we can't drop it, and object is still valid after this strange "into_iter". Here we can see, that object is still valid after such function call:

Also we can see, that v from for v in dst is a reference.

Could you provide some examples, where into_iter implementation for object reference is useful?

I can't understand the purpose of this implementation. If we have a mutable reference to object, we can't drop it, …

The general purpose of IntoIterator is not to consume a collection and possibly drop its elements. The general purpose of IntoIterator is to take some value (which may or may not be a reference) and turn it into an iterator, however that makes sense for that type of value.

IntoIterator implementations on references are how generic code does the equivalent of .iter() and .iter_mut(), when accepting a generic collection-like type.

Rust could instead have separate IntoRefIterator and IntoMutIterator traits, but that would not provide any benefit, and the costs would be: additional complexity, and more mismatches needing adapters or additional impls when you want to pass something to iterator-accepting code that is neither a collection nor a reference to collection.

2 Likes

No, of course, it is not necessary to introduce new traits, they are already too much
I still can not understand 2 things:

  1. Why for for el in v can not be always iter_mut() for v: &mut T
  2. Why behavior of for el in v where v: &mut T is different when lifetime of v is outside of current function (not a scope). Looks like references have some another hidden property, not only lifetime. This behavior is very surprising, and I can not see the purpose of it.

Consider this program:

fn iter_by_ref<T>(input: &mut T) {
    for element in input { 
        // ...
    }
}

This generic function must have a trait bound that expresses “input can be iterated over”. What should that bound be, in your hypothetical? In Rust as it is, the required bound is

where
    for<'a> &'a mut T: IntoIterator

What bound would correspond to “just call iter_mut()”? What trait would iter_mut() belong to? Some possibilities are:

  • it's a separate method of IntoIterator (this would limit the ways IntoIterator can be implemented)
  • it's a separate IntoMutIterator trait
  • there is no bound, but the code doesn't compile if T doesn't have an iter_mut() method (Rust specifically chose not to have this kind of “duck typed generics”)
  • some completely different language feature

Implementing IntoIterator on references makes things work without needing more language features or more traits.

1 Like

We can see that type of v is a mutable reference, in this case we can ask IterMut for T.
In your example it is obvious that we have a mutable reference.
More hard example is:

fn iter_by_ref<T: IntoIterator>(input: T) {
    for element in input { 
        // ...
    }
}

Obviously, in this case we can call only into_iter(). I think, we should not pass references to such function. And the next example:

fn iter_by_ref<T: &mut Iterator >(input: T) {
    for element in input { 
        // ...
    }
}

Here we should call iter_mut(). I still do not see the problems of this approach.

Then IterMut must be a separate trait which T implements.

Even then, one of the principles of Rust is that we don't look at types and make decisions to do one thing or another, except via traits. If for did something different for mutable references than other types, and that was not expressed by implementing a trait, that would make Rust a more complex language by adding a special rule.

And, in the current Rust design, IntoIterator is the trait that expresses that choice. We don't need any more mechanism than that — it's the simplest approach that fits into the rest of Rust.

1 Like

Ok, got it. But...

that's true. It do something different for local mutable references.

It does not, as has already been pointed out. for always calls IntoIterator::into_iter on whatever you give it and iterates over whatever that Iterator's item type is. What for does doesn't change whether that thing is a reference or not. But what IntoIterator does for references can be different than for non-references because they are different types and have different implementations.

2 Likes

But thats wrong. For example, for el in &mut vector calls iter_mut()

No, it uses IntoIterator::into_iter(&mut vector). Which is implemented via a call to iter_mut.

8 Likes

To appreciate the generality of IntoIterator, observe that:

  • Vec<T> implements IntoIterator<Item=T>
  • &Vec<T> implements IntoIterator<Item=&T>
  • &mut Vec<T> implements IntoIterator<Item=&mut T>

and consequently,

  • vec.into_iter() implements Iterator<Item=T>
  • (&vec).into_iter() (which internally calls vec.iter()) implements Iterator<Item=&T>
  • (&mut vec).into_iter() (which internally calls vec.iter_mut()) implements Iterator<Item=&mut T>
6 Likes

Sorry , I just did not create correct example.

This example does not compile too:

fn foo() {
    let mut v = vec![1,2,3];
    let dst = &mut v;
    for el in dst {}
    for el in dst {}
}

So, behavior is consistent for local and arg references.
Thatks for all, now I understood, how it works.

The last question:
Why it works:

fn borrow<T>(_: &mut T) {}
fn foo() {
    let mut i = 0;
    let dst = &mut i;
    borrow(dst);
    borrow(dst);
}

But this does not:

fn borrow(_: T) {}
fn foo() {
    let mut i = 0;
    let dst = &mut i;
    borrow(dst);
    borrow(dst);
}

?

Special compiler reborrowing magic for reference argument?