Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some way to simulate &mut reborrows in user code #1403

Open
nikomatsakis opened this issue Dec 10, 2015 · 18 comments
Open

Some way to simulate &mut reborrows in user code #1403

nikomatsakis opened this issue Dec 10, 2015 · 18 comments
Labels
T-lang Relevant to the language team, which will review and decide on the RFC.

Comments

@nikomatsakis
Copy link
Contributor

@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).

@dgrunwald
Copy link
Contributor

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)

@carllerche
Copy link
Member

+1. Commenting to track.

@eddyb
Copy link
Member

eddyb commented Dec 23, 2015

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.

@carllerche
Copy link
Member

@eddyb In my case, my type has multiple fields. If I understand your comment, then implementing CoerceUnsized wouldn't work?

@eddyb
Copy link
Member

eddyb commented Dec 28, 2015

@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.

@SimonSapin
Copy link
Contributor

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.

@pnkfelix
Copy link
Member

cc me

@soltanmm
Copy link

soltanmm commented Mar 2, 2016

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).

@dwrensha
Copy link

This is part of what I was getting at in http://dwrensha.github.io/capnproto-rust/2014/12/27/custom-mutable-references.html

@nrc nrc added the T-lang Relevant to the language team, which will review and decide on the RFC. label Aug 19, 2016
@sgrif
Copy link
Contributor

sgrif commented May 9, 2017

Ran into a need for this today.

@dhardy
Copy link
Contributor

dhardy commented Mar 1, 2018

I've run into this twice now.

Edit: better example
Also see: rust-random/rand#287

@carllerche
Copy link
Member

This would also be super helpful for iovec which is used heavily as part of Mio / Tokio / the bytes crate, etc... for vectored operations.

@de-vri-es
Copy link

Ran into it too. It's really annoying since the workarounds are very verbose and hard to understand.

@newpavlov
Copy link
Contributor

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.

@ShadowJonathan
Copy link

Not to bikeshed, but what about (an incredibly verbose) &re T & &re mut T?

@dhardy
Copy link
Contributor

dhardy commented Aug 15, 2021

@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 re<'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.

@SimonSapin
Copy link
Contributor

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.

@dullbananas
Copy link

! is less verbose

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

No branches or pull requests