pub struct PCell<T: ?Sized> { /* private fields */ }Expand description
PCell<T> (which stands for “permissioned cell”) is the most primitive Verus Cell type.
It can often be used as a replacement for Rust’s UnsafeCell, and it can serve
as a basis for verifying many other interior-mutable types
(e.g., InvCell,
RefCell).
PCell uses a ghost permission token similar to simple_pptr::PPtr – see the simple_pptr::PPtr
documentation for the basics.
For PCell, the associated type of the permission token is PointsTo.
If you want a cell that can be optionally initialized, see pcell_maybe_uninit::PCell.
§Differences from PPtr.
Whereas simple_pptr::PPtr represents a fixed address in memory,
a PCell has no fixed address because a PCell might be moved.
As such, the pcell.id() does not correspond to a memory address; rather,
it is a unique identifier that is fixed for a given cell, even when it is moved.
The arbitrary ID given by pcell.id() is of type CellId.
Despite the fact that it is, in some ways, “like a pointer”, note that
CellId does not support any meangingful “pointer arithmetic,”
has no concept of a “null ID”,
and has no runtime representation.
§Differences from UnsafeCell.
Though inspired by UnsafeCell, PCell is not quite the same thing.
The differences include:
-
PCell<T>does not call the destructor ofTwhen it goes out of scope. Technically speaking,PCell<T>is actually implemented viaManuallyDrop<UnsafeCell<T>>. This is because dropping the contents is unsound if thePointsTo<T>is not also reclaimed. To drop aPCell<T>without leaking, callinto_innerwith the correspondingPointsTo. -
PCell<T>always implementsSendandSync, regardless of the typeT. Instead, it isPointsTo<T>where the marker traits matter. (It doesn’t matter if you move the bytes to another thread if you can’t access them.)
§Example
use vstd::prelude::*;
use vstd::cell::pcell as pc;
verus! {
fn example_pcell() {
let (cell, Tracked(mut points_to)) = pc::PCell::new(5);
assert(points_to.id() == cell.id());
assert(points_to.value() == 5);
cell.write(Tracked(&mut points_to), 17);
assert(points_to.id() == cell.id());
assert(points_to.value() == 17);
let x = cell.into_inner(Tracked(points_to));
assert(x == 17);
}
} // verus!Implementations§
Source§impl<T: ?Sized> PCell<T>
impl<T: ?Sized> PCell<T>
Sourcepub const exec fn new(v: T) -> pt : (PCell<T>, Tracked<PointsTo<T>>)where
T: Sized,
pub const exec fn new(v: T) -> pt : (PCell<T>, Tracked<PointsTo<T>>)where
T: Sized,
pt.1@.id() == pt.0.id() && pt.1@.value() == v,Construct a new PCell with a fresh id.
Sourcepub exec fn borrow<'a>(&'a self, Tracked(perm): Tracked<&'a PointsTo<T>>) -> v : &'a T
pub exec fn borrow<'a>(&'a self, Tracked(perm): Tracked<&'a PointsTo<T>>) -> v : &'a T
self.id() === perm.id(),ensuresv === perm.value(),Sourcepub exec fn replace(&self, Tracked(perm): Tracked<&mut PointsTo<T>>, in_v: T) -> out_v : Twhere
T: Sized,
pub exec fn replace(&self, Tracked(perm): Tracked<&mut PointsTo<T>>, in_v: T) -> out_v : Twhere
T: Sized,
self.id() === old(perm).id(),ensuresperm.id() === old(perm).id(),*perm.value() === in_v,out_v === *old(perm).value(),Sourcepub exec fn into_inner(self, Tracked(perm): Tracked<PointsTo<T>>) -> v : Twhere
T: Sized,
pub exec fn into_inner(self, Tracked(perm): Tracked<PointsTo<T>>) -> v : Twhere
T: Sized,
self.id() === perm.id(),ensuresv === *perm.value(),Trait Implementations§
impl<T: ?Sized> Send for PCell<T>
impl<T: ?Sized> Sync for PCell<T>
PCell is always safe to Send or Sync. Rather, it is the PointsTo object where Send and Sync matter.
(It doesn’t matter if you move the bytes to another thread if you can’t access them.)
Auto Trait Implementations§
impl<T> !Freeze for PCell<T>
impl<T> !RefUnwindSafe for PCell<T>
impl<T> Unpin for PCell<T>
impl<T> UnwindSafe for PCell<T>where
T: UnwindSafe + ?Sized,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T, U> IntoSpecImpl<U> for Twhere
U: From<T>,
impl<T, U> IntoSpecImpl<U> for Twhere
U: From<T>,
Source§impl<T, VERUS_SPEC__A> TryFromSpec<T> for VERUS_SPEC__Awhere
VERUS_SPEC__A: TryFrom<T>,
impl<T, VERUS_SPEC__A> TryFromSpec<T> for VERUS_SPEC__Awhere
VERUS_SPEC__A: TryFrom<T>,
Source§exec fn obeys_try_from_spec() -> bool
exec fn obeys_try_from_spec() -> bool
Source§impl<T, VERUS_SPEC__A> TryIntoSpec<T> for VERUS_SPEC__Awhere
VERUS_SPEC__A: TryInto<T>,
impl<T, VERUS_SPEC__A> TryIntoSpec<T> for VERUS_SPEC__Awhere
VERUS_SPEC__A: TryInto<T>,
Source§exec fn obeys_try_into_spec() -> bool
exec fn obeys_try_into_spec() -> bool
Source§impl<T, U> TryIntoSpecImpl<U> for Twhere
U: TryFrom<T>,
impl<T, U> TryIntoSpecImpl<U> for Twhere
U: TryFrom<T>,
Source§open spec fn obeys_try_into_spec() -> bool
open spec fn obeys_try_into_spec() -> bool
{ <U as TryFromSpec<Self>>::obeys_try_from_spec() }