Replicating &mut's "multiple uses without Clone"

I'm trying to make a type that wraps &mut u8 in a struct, in order to impose some additional invariants. In doing that, I realize I'm not able to make my struct follow the same borrow-checking rules as &mut does. Playground link: Rust Playground.

Here's the issue. When I have a &mut u8, I can pass it multiple times to a function:

fn f(r: &mut u8) {
    *r = 1;
}

fn g(r: &mut u8) {
    f(r);
    f(r);
}

However, when I wrap my &mut u8 in a struct and try to do the same thing, I get a type error:

struct S<'a>(&'a mut u8);

fn f2(r: S) {
    *r.0 = 1;
}

fn g2(r: S) {
    f2(r);
    f2(r);  // FAILS
}

with error:

error[E0382]: use of moved value: `r`
  --> src/lib.rs:18:8
   |
16 | fn g2(r: S) {
   |       - move occurs because `r` has type `S<'_>`, which does not implement the `Copy` trait
17 |     f2(r);
   |        - value moved here
18 |     f2(r);
   |        ^ value used here after move

The type error on S makes sense to me: I don't implement Clone on S, so I can't use it after it is moved. My questions:

  1. How is &mut able to work around this issue? It doesn't implement Clone, but it can be used multiple times.
  2. Is it possible to provide the same typechecking rules to my own structs? My fear is that &mut is imbued with some special rules that user-defined types have no access to.
1 Like

This is not something you can replicate in your own types. However you can define the following method:

impl<'a> S<'a> {
    fn as_mut<'b>(&'b mut self) -> S<'b> {
        S(self.0)
    }
}
3 Likes

The thing that &mut T supports implicitly is called “re-borrowing”. For custom types you’ll need to re-borrow explicitly with a method like the one @alice explained above. Then you call f(r.as_mut()) explicitly and it works.

I wouldn’t be surprised if – some day – support for implicitly re-borrowing custom types would become possible, but that’s thinking really long-term, I’m not sure if there’s even any proposal for such a feature.

Even the standard library’s own Pin<&mut T> has this shortcoming, and you’ll need to call Pin::as_mut to use it multiple times.

4 Likes

Thanks @alice and @steffahn, that's perfect. Much appreciated!

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.