-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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 Cell to non-Copy types #1651
Conversation
Oddly enough @SimonSapin, @nox, and I were just talking about this, and we ended up concluding that we probably wouldn't want to extend the I'd also personally err on the side of being conservative and drop |
# Motivation | ||
[motivation]: #motivation | ||
|
||
It allows safe inner-mutability of non-`Copy` types without the overhead of `RefCell`'s reference counting. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which types? What is a concrete use case for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://github.com/SimonSapin/kuchiki/blob/23e92d45b7406865e035defdc5f5ca9f1809c406/src/tree.rs#L116 is the best example I could find with a quick search.
Can you explain this remark? As far as I can tell it's not true for PartialEq, so I want to know what assorted traits you refer to :) |
@durka ah yeah sure! So right now the standard library has: impl<T: PartialEq + Copy> PartialEq for Cell<T> { ... } If, however, impl<T: PartialEq> PartialEq for Cell<T> { ... } Unfortunately, though, we can't implement that. This means that we'd probably have to have |
Oh I see. I thought you were saying that it was On Wed, Jun 15, 2016 at 1:11 PM, Alex Crichton [email protected]
|
I actually formalised I have various use cases for both in Servo. https://github.com/nox/mitochondria/blob/master/src/move.rs#L3-L46 |
```rust | ||
impl<T> Cell<T> { | ||
fn set(&mut self, val: T); | ||
fn replace(&mut self, val: T) -> T; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These should all be &self
right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops
Along with
we could also have
(the first two are symmetric with cc #1106 |
@alexcrichton You should be able to implement fn eq (&self, other: &Self) {
let self_ptr = self.as_unsafe_cell().get();
let other_ptr = other.as_unsafe_cell().get();
unsafe { &*self_ptr == &*other_ptr }
} Ah, wait, you could have some sort of way to access |
That is unsound because you call PartialEq from inside the UnsafeCell. |
} | ||
|
||
impl<T: Copy> Cell<T> { | ||
fn get(&self); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-> T
I think this needs to be in the detailed design:
pub fn set(&self, value: T) {
unsafe {
*self.value.get() = value;
}
} But for struct Evil(Box<u32>, Rc<MoveCell<Option<Evil>>>);
impl Drop for Evil {
fn drop(&mut self) {
mem::drop(self.1.take()); // Mess with the "other" node, which might be `self`.
self.0.clone(); // use after free!
}
}
let a = Rc::new(MoveCell::new(None));
a.replace(Some(Evil(Box::new(5), a.clone()))); // Make a reference cycle.
a.set(None); // Trigger Evil::drop while in the cell To avoid this, the method needs to be rewritten so that pub fn set(&self, value: T) {
self.value.replace(value); // semi-colon to drop the return value of replace
} |
@ubsan indeed, that is not sound, as the whole point of |
I'm in favor of having something like (I'd also like |
@rfcbot fcp close My personal preference would be to not enhance The second drawback mentioned is a showstopper for me. In general if you're working with a I'm going to propose that the libs team closes this RFC as a result of that drawback, but I'm curious to hear others' thoughts! |
Team member @alexcrichton has proposed to close this. The next step is review by the rest of the tagged teams: Concerns:
Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
Hmm. I am sad, because I think that having a plethora of |
I don't see why the There's no principle saying that every method/impl of a type should require the same trait bounds. I don't even understand why you would expect that. |
For me at least it's all about the surprise factor of using |
I agree with you on the general principle; but of course one has to evaluate each case on its merits. In this case particular case I feel conflicted. You're making me rethink my opinion a bit. Let me just throw out some various chaotic thoughts. Combining sharing and mutability is still a weak spotI feel like in general we need to smooth out our story on sharing + mutability. This begins with education: explaining better when it is legal to mutate (the naming of But also just looking at the APIs: The cell vs ref-cell split is logical, but not ideal. I think it's not well-explained, for one thing, but also it is often stricter than normal. Many uses of Moreover, and most importantly I think, I'd prefer if we had a widely usable alternative that avoided these pitfalls, so that we could promote it as the preferred way to have sharing and mutability (something like "avoid it if you can, but if not, use Note that there is nothing stopping me from prototyping these ideas in a crates.io crate except for laziness. =) What I would prefer: an expanded CellI would prefer if we could expand on Since we want to eliminate cell-related panics, we can't permit open-ended borrows like
The last few might seem surprising. The idea here is that we know that Note that I said Why partial eq bothers meOK, without that context, why do I care about Random middle groundI wonder if it'd be a good first step to just extend An irrelevant, whiny aside: why I wish cell did not have
|
I feel like there's a conceptual difference here between (Note that I'm not arguing against potentially adding the others as well at some point. Just against lumping them in with
I feel like the rationale here is much more straightforward than that: "Hey how come there isn't a "Try implementing it and find out." "Oh, I see. It seems like you can't express it using the API."
I agree with this completely and the same thought has occurred to me as well. (Another reference point is Haskell where |
Yep, good point. |
Regarding mental model / documentation ergonomics, I believe this will actually improve the situation: I was recently thinking about how to best explain I'm surprised that some people are so vehemently contesting the usefulness of this. (On the now-closed The specific use case that brought me here is for Unix file descriptors, which I want to wrap in a non-copy type. (While technically they are just a This is an interesting example I think, because it demonstrates that the difference between a low-cost abstraction and a zero-cost abstraction can be fundamental: if we could continue using |
Apologies for the long delay moving forward on this! Given the thrust of the overall discussion here, it seems both that there's a pretty decent motivation, and a reasonable mental model. @Amanieu, can you update the RFC to try to incorporate some of the discussion, in particular around the motivation and mental model issues? (No changes needed to the actual design.) Meanwhile, I'll kick off the process for heading toward FCP. @rfcbot fcp merge |
Team member @aturon has proposed to merge this. The next step is review by the rest of the tagged teams:
No concerns currently listed. Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
@rfcbot reviewed |
ping @BurntSushi, @brson, @sfackler, @withoutboats please review! |
ping @withoutboats, only review left |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
The FCP period has elapsed without any further discussion, so the RFC has now been merged! Thanks @Amanieu! |
The final comment period is now complete. |
Not sure if this is the right place to ask, but I have a question concerning this RFC: Why is |
Yeah, it seems sound to do that, it's probably still restricted because nobody thought of it. Might be worth opening up a quick rfc for this, or perhaps just bringing it up on the tracking issue. |
@RalfJung sounds good to me! (there's probably a few more methods that can be generalized as well) |
Also I don't think such a change would require an RFC any more |
Agreed, a PR is fine. |
All right, will prepare one. @alexcrichton well, the other two are taking a shared borrow of the Cell, so they are way more complicated to talk about. On the other hand, they are returning |
I think that only |
I did a comparison of the methods of
The main questions on top of my mind are:
It is totally fine to me if As niko stated above, the mental model of "Cell supports moving bits around, but never gives a reference to the inside" seems pretty coherent, before this RFC is merged. But after this RFC is merged, the distinction between these two types are very vague, and the motivation of this separation seems not strong enough. To make cell types as easy as it can be, I'm proposing to merge the methods together and make these two types exactly identical. We can update the mental model to: if you want to move bits around, use |
@F001 yes, it is necessary to have two, the inner implementation differs and the guarantees provided are different. |
@F001 To expand a bit on Manish’s explanation: an important invariant of safe Rust is that mutation cannot happen while there multiple ways (aliasing) to access something. This leads to the separation of
This tradeoff is why they both exist. And the different constraints explain the different API. Except for |
@SimonSapin I see. Thank you all for clarification. |
|
This RFC extends the
Cell
type to work with non-Copy
types.Rendered