-
Notifications
You must be signed in to change notification settings - Fork 13.9k
RFC: Introduce Mut<T> to libstd (round 2) #10514
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
Conversation
src/libstd/mutable.rs
Outdated
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
Mutencapsulates a mutable, non-nullable slot. TheCelltype is currently used to do this, butCellis much more commonly used as a workaround for the inability to move values into non-once functions.Mutprovides a more robust API.Mutduplicates the semantics of borrowed pointers with enforcement at runtime instead of compile time.borrowreturns aReftype andborrow_mutreturns aRefMuttype, 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_borrowandtry_borrow_mutmethods 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.ReadPtrandWritePtrhave been renamed toRefandRefMutrespectively, asRefis toref fooandRefMutis toref mut fooasMutis tomut foo.getonMutRefnow takes&selfinstead of&mut selffor consistency with&mut. As @alexcrichton pointed, out this violates soundness by allowing aliasing&mutreferences.Cellis being left as is. It solves a different problem thanMutis designed to solve.Mut<Option<T>>. SinceCellisn'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 ofimplis very uncommon in the standard library.Open Questions
Cellshould now be used exclusively for movement into closures. Should this be enforced by reducing its API tonewandtake? It seems like this use case will be completely going away once the transition toprocand co. finishes.try_mapandtry_map_mutmethods along withmapandmap_mut?