-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
RFC: Introduce Mut<T> to libstd (round 2) #10514
Conversation
/// | ||
/// Fails if the value is currently mutably borrowed. | ||
#[inline] | ||
pub fn map<U>(&self, blk: |&T| -> U) -> U { |
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.
Shouldn't this be called with
and with_mut
? It doesn't seem like there's any mapping going on here.
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.
Done
I'm very much a fan of destroying I would not vote for a |
Agree On Sat, Nov 16, 2013 at 3:25 AM, Alex Crichton [email protected]:
|
Is there any reason not to add |
@alexcrichton I'm hesitant to totally replace |
Sounds reasonable to me, how do you feel about reducing |
Will do |
You can't call associated functions on typedefs, so it'd be weirdly On Sat, Nov 16, 2013 at 6:47 AM, Gábor Lehel [email protected]:
|
I am in favor of this PR, but I'd like to do a thorough review. I think we'll need a few more compile-fail tests, if nothing else. |
@alexcrichton I've stripped @nikomatsakis I've added a couple more compile-fail soundness tests. |
|
That is a problem. We could do something like impl<T: Freeze> Rc<Mut<T>> {
fn from_mut(val: Mut<T>) -> Rc<Mut<T>> { ... }
} |
@sfackler: I don't think we need a |
@thestinger yeah, I just realized that and edited the comment. |
@sfackler: Is that usable as |
@thestinger yep, it is. I've updated the PR. |
My current thinking is that:
|
After some discussion on IRC, we decided that while @pcwalton's scheme of using a "tag-less" There are some ways to overcome this: one option that appeals to me is to define a "POD" builtin bound, basically meaning any type that can be safely memcopied, and then say that Presuming we do wind up with two types, then |
I think "Mut" is a much more helpful name than "Cell". You use it to introduce the ability to mutate. What association is "Cell" supposed to evoke? |
The prison cell you're going to be sent to for bending the mutability rules :). |
You have my blessings. Not that it's needed, but still. |
The consensus we came to is that this should be named @glehel The problem here is that there are multiple things called |
|
@pcwalton updated. |
(I think reading or writing the name is a much more common use case than saying or hearing it, but w/e.) |
Based off of blake2-ppc's work in rust-lang#9429.
Rc<Mut<T>> should be used instead
This is based off of @blake2-ppc's work on #9429. That PR bitrotted and I haven't been able to contact the original author so I decided to take up the cause. Overview ====== `Mut` encapsulates a mutable, non-nullable slot. The `Cell` type is currently used to do this, but `Cell` is much more commonly used as a workaround for the inability to move values into non-once functions. `Mut` provides a more robust API. `Mut` duplicates the semantics of borrowed pointers with enforcement at runtime instead of compile time. ```rust let x = Mut::new(0); { // make some immutable borrows let p = x.borrow(); let y = *p.get() + 10; // multiple immutable borrows are allowed simultaneously let p2 = x.borrow(); // this would throw a runtime failure // let p_mut = x.borrow_mut(); } // now we can mutably borrow let p = x.borrow_mut(); *p.get() = 10; ``` `borrow` returns a `Ref` type and `borrow_mut` returns a `RefMut` type, both of which are simple smart pointer types with a single method, `get`, which returns a reference to the wrapped data. This also allows `RcMut<T>` to be deleted, as it can be replaced with `Rc<Mut<T>>`. Changes ====== I've done things a little bit differently than the original proposal. * I've added `try_borrow` and `try_borrow_mut` methods that return `Option<Ref<T>>` and `Option<RefMut<T>>` respectively instead of failing on a borrow check failure. I'm not totally sure when that'd be useful, but I don't see any reason to not put them in and @cmr requested them. * `ReadPtr` and `WritePtr` have been renamed to `Ref` and `RefMut` respectively, as `Ref` is to `ref foo` and `RefMut` is to `ref mut foo` as `Mut` is to `mut foo`. * `get` on `MutRef` now takes `&self` instead of `&mut self` for consistency with `&mut`. As @alexcrichton pointed, out this violates soundness by allowing aliasing `&mut` references. * `Cell` is being left as is. It solves a different problem than `Mut` is designed to solve. * There are no longer methods implemented for `Mut<Option<T>>`. Since `Cell` isn't going away, there's less of a need for these, and I didn't feel like they provided a huge benefit, especially as that kind of `impl` is very uncommon in the standard library. Open Questions ============ * `Cell` should now be used exclusively for movement into closures. Should this be enforced by reducing its API to `new` and `take`? It seems like this use case will be completely going away once the transition to `proc` and co. finishes. * Should there be `try_map` and `try_map_mut` methods along with `map` and `map_mut`?
Introduce {Ref, RefMut}::try_map for optional projections in RefCell This fills a usability gap of `RefCell` I've personally encountered to perform optional projections, mostly into collections such as `RefCell<Vec<T>>` or `RefCell<HashMap<U, T>>`: > This kind of API was briefly featured under Open questions in rust-lang#10514 back in 2013 (!) ```rust let values = RefCell::new(vec![1, 2, 3, 4]); let b = Ref::opt_map(values.borrow(), |vec| vec.get(2)); ``` It primarily avoids this alternative approach to accomplish the same kind of projection which is both rather noisy and panicky: ```rust let values = RefCell::new(vec![1, 2, 3, 4]); let b = if values.get(2).is_some() { Some(Ref::map(values.borrow(), |vec| vec.get(2).unwrap())) } else { None }; ``` ### Open questions The naming `opt_map` is preliminary. I'm not aware of prior art in std to lean on here, but this name should probably be improved if this functionality is desirable. Since `opt_map` consumes the guard, and alternative syntax might be more appropriate which instead *tries* to perform the projection, allowing the original borrow to be recovered in case it fails: ```rust pub fn try_map<U: ?Sized, F>(orig: Ref<'b, T>, f: F) -> Result<Ref<'b, U>, Self> where F: FnOnce(&T) -> Option<&U>; ``` This would be more in line with the `try_map` method [provided by parking lot](https://docs.rs/lock_api/0/lock_api/struct.RwLockWriteGuard.html#method.try_map).
Introduce {Ref, RefMut}::try_map for optional projections in RefCell This fills a usability gap of `RefCell` I've personally encountered to perform optional projections, mostly into collections such as `RefCell<Vec<T>>` or `RefCell<HashMap<U, T>>`: > This kind of API was briefly featured under Open questions in rust-lang#10514 back in 2013 (!) ```rust let values = RefCell::new(vec![1, 2, 3, 4]); let b = Ref::opt_map(values.borrow(), |vec| vec.get(2)); ``` It primarily avoids this alternative approach to accomplish the same kind of projection which is both rather noisy and panicky: ```rust let values = RefCell::new(vec![1, 2, 3, 4]); let b = if values.get(2).is_some() { Some(Ref::map(values.borrow(), |vec| vec.get(2).unwrap())) } else { None }; ``` ### Open questions The naming `opt_map` is preliminary. I'm not aware of prior art in std to lean on here, but this name should probably be improved if this functionality is desirable. Since `opt_map` consumes the guard, and alternative syntax might be more appropriate which instead *tries* to perform the projection, allowing the original borrow to be recovered in case it fails: ```rust pub fn try_map<U: ?Sized, F>(orig: Ref<'b, T>, f: F) -> Result<Ref<'b, U>, Self> where F: FnOnce(&T) -> Option<&U>; ``` This would be more in line with the `try_map` method [provided by parking lot](https://docs.rs/lock_api/0/lock_api/struct.RwLockWriteGuard.html#method.try_map).
Introduce {Ref, RefMut}::try_map for optional projections in RefCell This fills a usability gap of `RefCell` I've personally encountered to perform optional projections, mostly into collections such as `RefCell<Vec<T>>` or `RefCell<HashMap<U, T>>`: > This kind of API was briefly featured under Open questions in rust-lang#10514 back in 2013 (!) ```rust let values = RefCell::new(vec![1, 2, 3, 4]); let b = Ref::opt_map(values.borrow(), |vec| vec.get(2)); ``` It primarily avoids this alternative approach to accomplish the same kind of projection which is both rather noisy and panicky: ```rust let values = RefCell::new(vec![1, 2, 3, 4]); let b = if values.get(2).is_some() { Some(Ref::map(values.borrow(), |vec| vec.get(2).unwrap())) } else { None }; ``` ### Open questions The naming `opt_map` is preliminary. I'm not aware of prior art in std to lean on here, but this name should probably be improved if this functionality is desirable. Since `opt_map` consumes the guard, and alternative syntax might be more appropriate which instead *tries* to perform the projection, allowing the original borrow to be recovered in case it fails: ```rust pub fn try_map<U: ?Sized, F>(orig: Ref<'b, T>, f: F) -> Result<Ref<'b, U>, Self> where F: FnOnce(&T) -> Option<&U>; ``` This would be more in line with the `try_map` method [provided by parking lot](https://docs.rs/lock_api/0/lock_api/struct.RwLockWriteGuard.html#method.try_map).
This is based off of @blake2-ppc's work on #9429. That PR bitrotted and I haven't been able to contact the original author so I decided to take up the cause.
Overview
Mut
encapsulates a mutable, non-nullable slot. TheCell
type is currently used to do this, butCell
is much more commonly used as a workaround for the inability to move values into non-once functions.Mut
provides a more robust API.Mut
duplicates the semantics of borrowed pointers with enforcement at runtime instead of compile time.borrow
returns aRef
type andborrow_mut
returns aRefMut
type, both of which are simple smart pointer types with a single method,get
, which returns a reference to the wrapped data.This also allows
RcMut<T>
to be deleted, as it can be replaced withRc<Mut<T>>
.Changes
I've done things a little bit differently than the original proposal.
try_borrow
andtry_borrow_mut
methods that returnOption<Ref<T>>
andOption<RefMut<T>>
respectively instead of failing on a borrow check failure. I'm not totally sure when that'd be useful, but I don't see any reason to not put them in and @cmr requested them.ReadPtr
andWritePtr
have been renamed toRef
andRefMut
respectively, asRef
is toref foo
andRefMut
is toref mut foo
asMut
is tomut foo
.get
onMutRef
now takes&self
instead of&mut self
for consistency with&mut
. As @alexcrichton pointed, out this violates soundness by allowing aliasing&mut
references.Cell
is being left as is. It solves a different problem thanMut
is designed to solve.Mut<Option<T>>
. SinceCell
isn't going away, there's less of a need for these, and I didn't feel like they provided a huge benefit, especially as that kind ofimpl
is very uncommon in the standard library.Open Questions
Cell
should now be used exclusively for movement into closures. Should this be enforced by reducing its API tonew
andtake
? It seems like this use case will be completely going away once the transition toproc
and co. finishes.try_map
andtry_map_mut
methods along withmap
andmap_mut
?