Some way to simulate `&mut` reborrows in user code
nikomatsakis opened this issue · 17 comments
@carllerche wanted a way to make a newtyped &mut
that would reborrow in the same implicit way that reborrows do today. That is, with an &mut
, foo(ptr)
is roughly equivalent to foo(&mut *ptr)
, but if ptr
is MyRef<'a>(&'a mut ...)
, then this will not coerce from MyRef<'a>
to MyRef<'b>
(where 'a:'b
).
In one of the designs for rust-cpython that I explored; I ran into a similar issue:
I had a zero-sized struct (newtype around PhantomData<&mut 'p ()>
), which could not be Copy
(unsafe code relied on the fact that user code had access to at most one instance of the struct at a time).
But this struct still had to be passed around between function calls; which needs something like re-borrowing to avoid the code becoming unwieldy.
In my case, I ended up going with a different design where the struct could be Copy
instead. (although there were more reasons for that than the lack of reborrowing)
+1. Commenting to track.
We could handle this similarly to unsizing coercions - see Rc
's CoerceUnsized
impl, which "only" require that it has a single field also implementing CoerceUnsized
, and this repeats all the way down to the primitive impls (specified in #982).
So if we generalized, impl<T: ?Sized, U: ?Sized> Coerce<Rc<U>> for Rc<T>
would require that *mut T: Coerce<*mut U>
which can be satisfied with an T: Unsize<U>
bound.
And impl<'a, 'b> Coerce<MyRef<'b>> for MyRef<'a>
would require 'a: 'b
and borrowck would treat it as a reborrow.
Should it work on multiple fields, including mixed coercions, e.g. one unsizing and one reborrowing, in different fields?
FWIW, we currently kind of support reborrowing combined with unsizing due to the way the CoerceUnsized
impls for &T
and &mut T
are generic over both input and output lifetimes, so impl CoerceUnsized<MyRef<'b, U>> for MyRef<'a, T>
would work, but borrowck may not understand it as a reborrow.
@eddyb In my case, my type has multiple fields. If I understand your comment, then implementing CoerceUnsized
wouldn't work?
@carllerche implementing CoerceUnsized
doesn't work unless you have an unsizing and it doesn't even result in a reborrow.
I'm talking about extending the mechanism to allow all coercions to happen inside user-defined types, with opt-in at the definition site.
Personally, I think Option<&mut T>
having reborrows would be quite handy.
Personally, I think Option<&mut T> having reborrows would be quite handy.
Yes, please. In one piece of code that really should have used Option<&mut HashSet<Name>>
I’ve ended up using &mut Option<HashSet<Name>>
in order to get re-borrows.
cc me
This is sort of possible today. Consider the following (messy, off-the-top-of-my-head, borderline embarrassing) associated-type-encoded-HKTs-based formulation:
pub trait Reborrowable<'a> {
type Result;
}
pub trait ReborrowMut<'self_>: for<'a> Reborrowable<'a> + Reborrowable<'self_, Result=Self> {
fn reborrow<'reborrow>(&'reborrow mut self)
-> <Self as Reborrowable<'reborrow>>::Result
where 'self_: 'reborrow;
}
This can be used as so: https://gist.github.com/anonymous/4aa1bfddaf5efdb53b15 (sans the type-identity trait bound to ensure Reborrowable
is well-behaved; I believe that particular bit not working is merely a bug rather than a missing feature).
I'm not saying that this is a solution - far from it. Still, it's possible to within a single function call on a trait (which is just about what the Deref
trait does with autoderef'ing).
This is part of what I was getting at in http://dwrensha.github.io/capnproto-rust/2014/12/27/custom-mutable-references.html
Ran into a need for this today.
Edit: better example
Also see: rust-random/rand#287
This would also be super helpful for iovec
which is used heavily as part of Mio / Tokio / the bytes crate, etc... for vectored operations.
Ran into it too. It's really annoying since the workarounds are very verbose and hard to understand.
I've also run into it.
Is there a reason why a simple Reborrow
marker trait would not work? For compiler it can behave as Copy
, but with additional restriction on having two copies at the same time.
Not to bikeshed, but what about (an incredibly verbose) &re T
& &re mut T
?
@newpavlov I would honestly recommend just using a reborrow method for now, like this one:
impl<'a, DS: DrawSharedImpl> DrawIface<'a, DS> {
/// Reborrow with a new lifetime
pub fn reborrow<'b>(&'b mut self) -> DrawIface<'b, DS>
where
'a: 'b,
{
DrawIface {
draw: &mut *self.draw,
shared: &mut *self.shared,
pass: self.pass,
}
}
}
Having an official trait for this and having it used automatically might be nice but isn't a big deal.
BTW we ran into it years ago with the Rand project (something to do with passing R: Rng
where &mut Rng
impls Rng
I think, but don't remember the details).
@ShadowJonathan I don't see the point of special syntax. As far as I'm concerned the point is to make the usual syntax for reborrows work for "reference wrappers" so that users don't have to think about it.
If it were possible to write custom impls for the
Pointee
This is custom DSTs. I believe there’s a lot more language design involved than "just allow Pointee
impls". Multiple RFCs have attempted to define this already, as you probably know.
Please let’s keep this issue about re-borrowing of existing types like Option<&mut Foo>
. If and when custom DSTs are added, whatever is done here should apply too.