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

Extend interior mutability (e.g. Cell) for better composition with other types. #1106

Open
eddyb opened this issue May 6, 2015 · 7 comments
Labels
T-lang Relevant to the language team, which will review and decide on the RFC.

Comments

@eddyb
Copy link
Member

eddyb commented May 6, 2015

A couple of ideas were outlined in this reddit comment.

1: Implementing the API for Cell<Rc<T>> and Cell<Vec<T>>: requires a lot of unsafe.

There might be a way to provide mutable access to the contents of Cell via Cell::with_mut(|mut_ref| {...}) and have the compiler prove that the closure cannot reach the Cell::with_mut entry point, but it's not something we can express right now.

2: Turning &mut T into multiple aliases with Cell semantics:

impl<T: Copy> Cell<T> {
    fn from_mut(x: &mut T) -> &Cell<T> {
        unsafe { mem::transmute(x) }
    }
}

I am not certain this is UB-free, but if it isn't, I still expect @glaebhoerl's CellRef<'a, T> suggestion to work.

cc @nikomatsakis

@Kimundi
Copy link
Member

Kimundi commented May 7, 2015

Hm, would it work to make Cell depend on a unsafe trait CellSafe that is implemented for all Copy types but that can also be implemented for custom ones?

struct Cell<T: CellSafe> { ... }

unsafe trait CellSafe { }

impl<T: Copy> CellSafe for T {}

impl CellSafe for SomeNonCopyType {}

@eddyb
Copy link
Member Author

eddyb commented May 7, 2015

set, replace and perhaps a few other operations are valid for any T - even if destructors can do something unsafe, you just need to put a new value in and drop the old one outside of the Cell.

So I assume you are talking abput get and Clone: the unsafe trait would work there, yes, and it would enable the use of Cell<Rc<T>>.

However, to fully implement an API for Cell<Vec<T>> where T: CellSafe, you would need unsafe code for each method. And that's just one type, it won't really scale.

Implementing a "never calls Cell::with_mut" check in the compiler does not seem particularly hard, but I'm not sure how you would pass a function as a type parameter of a trait. typeof?

@glaebhoerl
Copy link
Contributor

@eddyb So I came back here from the other thread and I think still don't really understand the with_mut idea, and might benefit from having it spelled out more explicitly. What's the type signature? Something like fn with_mut<T, U>(&Cell<T>, FnOnce(&mut T) -> U) -> U? Isn't that plainly UB what with the other reference to the Cell aliasing the &mut into it?

@eddyb
Copy link
Member Author

eddyb commented Jun 26, 2015

@glaebhoerl sorry, I have no idea how I missed your comment here.
Yes, that signature is correct, if you add the requirement that the closure cannot possibly call with_mut (e.g. any indirect calls would be outright disallowed).

That means the &Cell<T> reference is harmless (assuming all exposed accesses go through with_mut), not unlike &RefCell<T> when holding a &mut T to its contents. In that case, re-entrance is prevented at runtime (further borrows fail while the mutable one is active).

@glaebhoerl
Copy link
Contributor

It's not just with_mut, but calling back into set/swap could also violate memory safety. Not sure about get, not as directly in that case, but it would sure violate the "&muts aren't aliased" guarantee, who knows what the optimizer might do...

@eddyb
Copy link
Member Author

eddyb commented Jun 26, 2015

@glaebhoerl set, swap and get would all go through with_mut, sorry that wasn't very clear.
For this to work, with_mut has to be the one and only access of the embedded UnsafeCell.

@dgrunwald
Copy link
Contributor

I like the idea of

    fn from_mut(x: &mut T) -> &Cell<T> {
        unsafe { mem::transmute(x) }
    }

Can we clarify whether that transmute is UB?
I think currently rust doesn't guarantee that the transmute is valid ("struct layout is unspecified" and all).

Note that similar transmutes of questionable validity already appear in a few places in libstd (e.g. impl Deref for CString transmutes from &[u8] to &CStr).

It would be nice to officially have a rule that it's valid to transmute from &T to &S, and from &mut T to &mut S given struct S { single_member: T }.
I'm not sure if this rule is currently valid, particularly in the mutable case.

This rule would imply it's OK to transmute &mut T -> &mut UnsafeCell<T> -> &mut Cell<T> -> &Cell<T> (at least within libcore where we know how UnsafeCell and Cell are implemented).

@nrc nrc added the T-lang Relevant to the language team, which will review and decide on the RFC. label Aug 30, 2016
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

5 participants