I've been adapting some old code I wrote, one of them had the following (simplified):
pub fn a(x: &mut i32) {
for i in 0..10 {
b(x);
}
}
pub fn b(_x: &mut i32) {
}
which worked fine, even though &mut i32
isn't Copy
.
I wanted to restrict what methods could be called on the underlying type, (as instead of &mut i32
I had something along the lines of &mut Vec<...>
), so I created a wrapper type over the mutable reference:
#[derive(Debug)]
pub struct I32RefMut<'a>(&'a mut i32);
And I attempted to rewrite a
and b
using this wrapper as follows:
pub fn a2(x: I32RefMut) {
for _i in 0..10 {
b2(x);
}
}
pub fn b2(_x: I32RefMut) {
}
This gives the following error
17 | pub fn a2(x: I32RefMut) {
| - move occurs because `x` has type `I32RefMut<'_>`, which does not implement the `Copy` trait
18 | for _i in 0..10 {
19 | b2(x);
| ^ value moved here, in previous iteration of loop
Which is understandable, as x
gets moved into b2
on the first iteration of the loop.
Unfortunately I cannot implement Clone
nor Copy
, as there may only be 1 mutable reference to the object at a time.
My question is how does &mut i32
work around this and how can I implement this workaround (or similar) on my type I32RefMut
.
If possible I'd like to avoid unsafe code as much as possible, such as using #[repr(transparent)] struct I32Wrapper(i32)
and then transmuting &mut i32
to &mut I32Wrapper
, unless a safe wrapper of this type of operation exists already.
EDIT:
Found a "hack" solution, but I'm not very happy about how it looks, so I'll leave the question open. If no other solutions are found, I'll post it as an answer.
If the call to b2
is changed to b2( I32RefMut(x.0) )
, then it successfully compiles. This however, cannot be generalised to a function as such:
impl<'a> I32RefMut<'a> {
pub fn my_clone<'b: 'a>(&'b mut self) -> I32RefMut<'b> {
I32RefMut( self.0 )
}
}
As when we try to call it the compiler tells us we can't borrow x
mutably twice.
As this wrapper type is supposed to be defined in a library, I cannot expose it's internal reference, as the whole point of the wrapper was to restrain what the user can call on the reference.