Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a CloneMut trait?

An easily overlooked feature of clone() is that it can shorten the lifetimes of any references hidden inside the value being cloned. This is usually useless for immutable references, which are the only kind for which Clone is implemented.

It would, however, be useful to be able to shorten the lifetimes of mutable references hidden inside a value. Is there something like a CloneMut trait?

I've managed to write one. My question is whether there is a trait in the standard library that I should use instead, i.e. am I reinventing the wheel?

The rest of this question consists of details and examples.

Playground.

Special case: the type is a mutable reference

As a warm-up, the following is good enough when the type you're cloning is a mutable reference, not wrapped in any way:

fn clone_mut<'a, 'b: 'a>(q: &'a mut &'b mut f32) -> &'a mut f32 {
    *q
}

See this question (where it is called reborrow()) for an example caller.

Special case: the reference type, though user-defined, is known

A more interesting case is a user-defined mutable-reference-like type. Here's how to write a clone_mut() function specific to a particular type:

struct Foo<'a>(&'a mut f32);

impl<'b> Foo<'b> {
    fn clone_mut<'a>(self: &'a mut Foo<'b>) -> Foo<'a> {
        Foo(self.0)
    }
}

Here's an example caller:

fn main() {
    let mut x: f32 = 3.142;
    let mut p = Foo(&mut x);
    {
        let q = p.clone_mut();
        *q.0 = 2.718;
    }
    println!("{:?}", *p.0)
}

Note that this won't compile unless q gets a shorter lifetime than p. I'd like to view that as a unit test for clone_mut().

Higher-kinded type?

When trying to write a trait that admits both the above implementations, the problem at first feels like a higher-kinded-type problem. For example, I want to write this:

trait CloneMut {
    fn clone_mut<'a, 'b>(self: &'a mut Self<'b>) -> Self<'a>;
}

impl CloneMut for Foo {
    fn clone_mut<'a, 'b>(self: &'a mut Self<'b>) -> Self<'a> {
        Foo(self.0)
    }
}

Of course that's not allowed in Rust (the Self<'a> and Self<'b> parts in particular). However, the problem can be worked around.

General case

The following code compiles (using the preceding definition of Foo<'a>) and is compatible with the caller:

trait CloneMut<'a> {
    type To: 'a;

    fn clone_mut(&'a mut self) -> Self::To;
}

impl<'a, 'b> CloneMut<'a> for Foo<'b> {
    type To = Foo<'a>;

    fn clone_mut(&'a mut self) -> Self::To {
        Foo(self.0)
    }
}

It's a little ugly that there is no formal relationship between Self and Self::To. For example, you could write an implementation of clone_mut() that returns 77, completely ignoring the Self type. The following two attempts show why I think the associated type is unavoidable.

Attempt 1

This compiles:

trait CloneMut<'a> {
    fn clone_mut(&'a mut self) -> Self;
}

impl<'a> CloneMut<'a> for Foo<'a> {
    fn clone_mut(&'a mut self) -> Self {
        Foo(self.0)
    }
}

However, it's not compatible with the caller, because it does not have two distinct lifetime variables.

error[E0502]: cannot borrow `*p.0` as immutable because `p` is also borrowed as mutable

The immutable borrow mentioned in the error message is the one in the println!() statement, and the mutable borrow is the call to clone_mut(). The trait constrains the two lifetimes to be the same.

Attempt 2

This uses the same trait definition as attempt 1, but a different implementation:

trait CloneMut<'a> {
    fn clone_mut(&'a mut self) -> Self;
}

impl<'a, 'b: 'a> CloneMut<'a> for Foo<'b> {
    fn clone_mut(&'a mut self) -> Self {
        Foo(self.0)
    }
}

This doesn't even compile. The return type has the longer lifetime, and can't be made from the argument, which has the shorter lifetime.

Moving the lifetime parameter onto the method declaration gives the same error:

trait CloneMut {
    fn clone_mut<'a>(&'a mut self) -> Self;
}

impl<'b> CloneMut for Foo<'b> {
    fn clone_mut<'a>(&'a mut self) -> Self {
        Foo(self.0)
    }
}

Relationship with Clone

Incidentally, notice that CloneMut<'a, To=Self> is strictly stronger than Clone:

impl<'a, T: 'a> CloneMut<'a> for T where T: Clone {
    type To = Self;

    fn clone_mut(&'a mut self) -> Self {
        self.clone()
    }
}

That's why I think "CloneMut" is a good name.

like image 901
apt1002 Avatar asked Nov 09 '22 00:11

apt1002


1 Answers

The key property of &mut references is that they are unique exclusive references.

So it's not really a clone. You can't have two exclusive references. It's a reborrow, as the source will be completely unusable as long as the "clone" is in scope.

like image 120
Kornel Avatar answered Dec 03 '22 15:12

Kornel