Useful case for &mut &x?

Hi all, maybe someone has seen this:

fn main() {
  let mut vec = vec![1, 2, 3];
  let reff = &mut &vec;
}

Is there any useful case to use this? we could any other simpler solution, but I suppose there is something on this, right?

Edited: Some clippy issues, changed the example code.

Both don't work for me (which imho is correct), although for different reasons (mutability vs integer deref) - can you share the exact compiler version / system you are running this on or a playground link where you demonstrate the issue?

error[E0596]: cannot borrow `**refi` as mutable, as it is behind a `&` reference
 --> src/main.rs:5:3
  |
5 |   refi[0] = 20;
  |   ^^^^ cannot borrow as mutable

(Just as an aside: you can't use ref as a variable name since it's also a keyword, without using the r#ref syntax)

(If you were trying to compile it with both lines present, be aware that the error about the *refi[0] might "mask" the "previous" error)

I think you accidentally linked to the wrong playground.

sorry! I forgot to not use ref on the code, you are right about that.

About why it does not compile... seems was a clippy problem, it showed no error on the code.

So, going back, is it useful in some way a &mut &x?

Thanks, I accidentally copied the link to the playground and not the permalink :man_facepalming:

1 Like

Yes, but rarely. For example &mut &[u8] could be used to "consume" parts of the slice (replacing it with a shorter subslice of itself), which is used in the Read implementation for &[u8]. Though to be fair, if this wasn't done to conform to the existing, more general trait inferface here, one would typically avoid such API.

A major issue with &mut &.. is that when you mutate some Copy-able type, users of the API can easily mess up and call the method only on a copy of the thing that they actually meant to mutate. This is the reason why e. g. iterator generally shouldn't also implement Copy.

11 Likes

how is that? we could use &[u8] instead of the mut one, or Am'I missing something on the mut part that could be useful?.

I mean there are probably some valid usecases, but pretty rarely. For example, if you are only handling static strings, you could do (for whatever reason):

let string_ref = &mut "abc";

*string_ref = "abcd"

Note that this is not replacing the actual string, but rather where the ref is pointing to - the &mut &T is a mutable reference to a reference to something, meaning you can replace/change the reference but not actually change the T.

3 Likes

&[T] is a slice, meaning a fat pointer with a pointer to the data and some length. (since [T] is a dst (dynamically sized type)) - meaning you can imagine &[T] as something along the lines of a (const* (),usize) "tuple" - if you are able to change the ref, having a mutable reference to it, you can change the stored size inside that fat pointer, or the start of the slice itsself(the actual pointer), but not what's actually stored at the underlying memory of the slice.

Or as an example how you could use that:

let foo = [0,1,2,3,4,5,6,7,8,9];

let mut some_section = &foo[0..5];

dbg!(some_section); // [0,1,2,3,4]

fn trim_leading_zeroes_inplace(slice: &mut &[i32]) {
    let leading_zeroes_count = slice.into_iter().take_while(|x| **x == 0).count();

    *slice = &(**slice)[leading_zeroes_count..];
}

trim_leading_zeroes_inplace(&mut some_section);
dbg!(some_section); // [1,2,3,4]
dbg!(foo) // still [0,1,2,3,4,5,6,7,8,9] - we only changed the part we were looking at with `some_section`

Calling next on an iterator that returns shared references generally involves something that's akin to a &mut &_. Generalizing the Read example, any implementation of a trait for &SomeType may involve a &mut &SomeType due to the method signatures.

4 Likes

I think there's something generalizable here: because &mut &T can only change between pointing to different Ts that already exist[1], &mut &T will tend to appear only in use cases that are in some way sequentially processing data that is already in memory, and which don't fit Iterator.


  1. unless you use an arena allocator or other techniques to add more data with the same lifetime ↩︎

1 Like

An the other case of &mut &Type is when Type has interior mutability, and you want to provide an implementation for an existing interface that assumes normal mutability (and thus declares &mut self methods) via the trick to just (also) offer an impl … for &Type. (This is the Read for &File case.)

3 Likes

The case I mentioned also covers "emulating &self with self: &mut &This" (which can be useful when you don't control the trait and, e.g., want things to be usable with Arc<This> etc).

Ah, true, that is a common case. Though, those are cases where &muts are created and passed around but (usually) never strictly used to mutate. So a better statement is: &mut &T that is actually used to mutate (replace) the &T is usually used in some sort of sequential processing of data that is already in memory.

2 Likes

It's useful only if you want to change where the inner reference points to.

It happens to be useful for &mut &[u8], because it supports io::Read, because double reference allows shrinking the nested slice to advance parts that have been read.

3 Likes

Here’s another (somewhat contrived) example that fits this pattern and starts from the original code as a base, pairing items from both ends of the list without losing track of the unconsumed items in the middle:

fn iter_outside<'short, 'long, T>(
    items: &'short mut &'long [T],
) -> impl 'short + Iterator<Item = [&'long T; 2]> {
    std::iter::from_fn(move || {
        let [a, inner @ .., b] = *items else {
            return None;
        };
        *items = inner;
        Some([a, b])
    })
}

fn main() {
    let mut vec = vec![1, 2, 3];
    let reff = &mut &vec[..];

    for [a, b] in iter_outside(reff) {
        println!("Took {a}, {b}");
    }

    println!("Remainder {reff:?}");
}

thx all for the answers! I noticed I did not understood very well how variables works, so from the start I did not understood very well how mut/ref/non-mut things worked, talking this I noticed that and now I can see a lot better who is used and with the examples how to take advantage of this!

I still do not have 100% clear it, but a lot better, and would be other question if we dig that on this issue.

Thx :smiley:

Ah, that confused me for a second the way it was written. vec isn't mutated, so it doesn't need to be mutable; and reff relies on temporary lifetime extension; the extended temporary (holding &vec[..]) is the thing that tracks the state. I’d rather rewrite this instead as

fn main() {
    let vec = vec![1, 2, 3];
    let mut reff = &vec[..];

    for [a, b] in iter_outside(&mut reff) {
        println!("Took {a}, {b}");
    }

    println!("Remainder {reff:?}");
}

2 Likes

That’s a lot closer to what I would usually write, but in this case I was trying to keep @latot’s original code as intact as possible for expository reasons— I would have preferred to not even add the [..] but couldn’t think of a plausible use for &mut &Vec<_>.

1 Like

Ah! I missed that connection. Still, I think without also showing the more clean version, or explaining the temporary lifetime extension, I believe the code was a bit difficult to understand :wink:

1 Like