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

Pre-RFC: generalize std::cell::Cell to non-Copy types #1164

Closed
SimonSapin opened this issue Jun 17, 2015 · 12 comments
Closed

Pre-RFC: generalize std::cell::Cell to non-Copy types #1164

SimonSapin opened this issue Jun 17, 2015 · 12 comments
Labels
T-libs-api Relevant to the library API team, which will review and decide on the RFC.

Comments

@SimonSapin
Copy link
Contributor

I’ve been experimenting with a MoveCell type that’s a lot like std::cell::Cell, except it also works with types that are not Copy. When discussing it, the idea came up that at least some of its functionality could be added to Cell itself:

  1. The T: Copy bound is not required at all for Cell::new and Cell::set and could be removed.

  2. A replace method could be added (also without T: Copy):

    fn replace(&self, new_value: T) -> T { 
       unsafe { mem::replace(&mut *self.value.get(), new_value) }
    }
  3. More of MoveCell’s methods (such as peek where T: Default) could be added.

Does this sound like something we want to do with Cell, or is this functionality better off in a separate type? (Note that this separate type can and has been done outside of std on stable Rust.)

CC @alexcrichton @aturon

@eddyb
Copy link
Member

eddyb commented Jun 18, 2015

cc #1106

@cristicbz
Copy link

I always felt Cell was too limiting and you finally figured out what was missing! IMO peek & take seem a bit too specific & implicit for cell in general (Default is not necessarily an obvious choice and peek suggests a lightweight operation to me, while two memcpy-s are not necessarily so). Simply manually implementing peek() with two calls to replace when needed seems fine to me. Big 👍.

@SimonSapin
Copy link
Contributor Author

@cristicbz Yeah, I’m not super happy with the name of MoveCell::peek either. Do you have another suggestion?

@SimonSapin
Copy link
Contributor Author

Thinking a bit more, I’d like to have 1. and 2. in std but not 3., as I’m less confident in the closure-based API of peek (another idea is to use a RAII guard) and it can be done on top of replace with safe code.

@glaebhoerl
Copy link
Contributor

FWIW a more general way to formulate it would be something like (see also @eddyb's link):

#[derive(Clone, Copy)]
struct CellRef<'s, T> { ... }

impl CellRef<'s, T> {
    fn from_cell(cell: &'s Cell<T>) -> CellRef<'s, T> { ... }

    fn from_mut(mut_ref: &'s mut T) -> CellRef<'s, T> { ... }

    fn swap(self, other: CellRef<T>) { ... }

    fn set(self, value: T) { self.swap(CellRef::from_mut(&mut value)) }
}

impl<T: Copy> CellRef<'s, T> {
    fn get(self) -> T { ... }
}

(various details and annotations omitted)

This would subsume the existing Cell API as well as std::mem::swap and allow swaps between any combination of Cells and &muts.

Another thing you could do: while it's not safe to have references to the interiors of Cells (resp. CellRef targets) in general, it is safe in the case of "completely flat data which can't change its shape" - essentially non-allocating product types. So you could safely have CellRef<'s, (A, B)> -> (CellRef<'s, A>, CellRef<'a, B>) and similarly for other tuples and also structs, as well as fn at(self: CellRef<'s, [T]>, index: usize) -> CellRef<'s, T> and likewise for subslices.

@SimonSapin
Copy link
Contributor Author

Cell<T> implements some traits (Clone, PartialEq, Eq, Debug) with a T: Copy bound, making this generalized Cell less useful for non-Copy types. It is possible to rely on Default instead of Copy (temporarily replace with the default value while accessing, like MoveCell does) but changing this for Cell itself would be a breaking change for users that have T: Copy but not T: Default.

@eddyb
Copy link
Member

eddyb commented Jun 18, 2015

@glaebhoerl Any reason CellRef<'s, T> cannot be just &'s Cell<T>?
I've considered splitting tuples and slices before, but it would conflict with the proposed magicky "ask the compiler to enforce limitations on the callgraph for safety" with_mut.

@glaebhoerl
Copy link
Contributor

@eddyb I don't know. It seems weird to me. But maybe it's not. Don't have any deeper thoughts. :)

proposed magicky "ask the compiler to enforce limitations on the callgraph for safety" with_mut

Hm?

@SimonSapin
Copy link
Contributor Author

@glaebhoerl I don’t understand what CellRef is or does. Could you fill in the details?

@eddyb
Copy link
Member

eddyb commented Jun 18, 2015

@glaebhoerl the Cell::with_mut(|&mut T| ...) method I mentioned is the same one from #1106 (also see the reddit thread linked from there).
It would require the compiler to prove that the closure cannot call Cell::with_mut again.

Actually, splitting a Cell into sub-elements should still be safe in that scenario, as all operations would be implemented via Cell::with_mut so using any Cell in any way inside the closure wouldn't be allowed.

If there is another problematic case, related or not to the hypothetical Cell::with_mut, I cannot remember it, so 👍 on that idea.

@glaebhoerl
Copy link
Contributor

@SimonSapin It's a shared shallow-mutable reference type - you can copy it and get/set/swap through it, but not get interior references (except for flat compound types, as mentioned). Or in other words it's essentially equivalent to &Cell<T>, except it can also refer to an &mut-borrowed thing instead (either ensures there aren't interior references). What I wrote is basically the whole of the API for it.

@SimonSapin
Copy link
Contributor Author

Doing 1 and 2 is attractive, but having some operations only work in some cases (#1164 (comment)) makes it weird. MoveCell on crates.io is fine.

@petrochenkov petrochenkov added the T-libs-api Relevant to the library API team, which will review and decide on the RFC. label Jan 29, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-libs-api Relevant to the library API team, which will review and decide on the RFC.
Projects
None yet
Development

No branches or pull requests

6 participants