Is there a CloneMut property?

An easily overlooked feature of clone() is that it can shorten the life of any links hidden within the cloned value. This is usually not useful for immutable links, which are the only view for which Clone is implemented.

However, it would be useful to be able to shorten the lifetime of mutable links hidden within a value. Is there something like a CloneMut ?

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

The rest of this question consists of details and examples.

Playground

Special case: type is mutable reference

As a warm-up, the following is good enough if the type you are cloning is a mutable link, 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, although user-defined, is known

More interesting is a custom type with a mutable reference. 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 of the 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 will not compile if q does not shorten the lifetime than p . I would like to consider this as a unit test for clone_mut() .

Higher type?

When trying to write a tag that allows both of the above implementations, the problem first seems to be a higher type problem. For example, I want to write the following:

 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, this is not allowed in Rust (in particular, the parts Self<'a> and Self<'b> ). However, the problem can be solved.

General case

The following code compiles (using the previous 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 can write an implementation of clone_mut() that returns 77 , completely ignoring the Self type. The following two attempts show why I think a related type is inevitable.

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 is incompatible with the caller because it does not have two different lifetime variables.

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

The obligatory borrowing indicated in the error message is the statement in println!() , And mutable borrowing is the call to clone_mut() . This trait limits two lifetimes to the same.

Attempt 2

This uses the same attribute 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) } } 

It does not even compile. The return type has a longer service life and cannot be made from an argument that has a shorter service life.

Moving the lifetime parameter to a 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) } } 

Clone Relations

By the way, note that CloneMut<'a, To=Self> 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 is why I think " CloneMut " is a good name.

+5
source share
1 answer

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

So this is not a complete clone. You cannot have two exclusive links. This is a rewrite, since the source will be completely unusable while the "clone" is in scope.

0
source

Source: https://habr.com/ru/post/1265937/


All Articles