PCell

Struct PCell 

Source
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 of T when it goes out of scope. Technically speaking, PCell<T> is actually implemented via ManuallyDrop<UnsafeCell<T>>. This is because dropping the contents is unsound if the PointsTo<T> is not also reclaimed. To drop a PCell<T> without leaking, call into_inner with the corresponding PointsTo.

  • PCell<T> always implements Send and Sync, regardless of the type T. Instead, it is PointsTo<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>

Source

pub uninterp fn id(&self) -> CellId

A unique ID for the cell.

Source

pub const exec fn new(v: T) -> pt : (PCell<T>, Tracked<PointsTo<T>>)
where T: Sized,

ensures
pt.1@.id() == pt.0.id() && pt.1@.value() == v,

Construct a new PCell with a fresh id.

Source

pub exec fn borrow<'a>(&'a self, Tracked(perm): Tracked<&'a PointsTo<T>>) -> v : &'a T

requires
self.id() === perm.id(),
ensures
v === perm.value(),
Source

pub exec fn replace(&self, Tracked(perm): Tracked<&mut PointsTo<T>>, in_v: T) -> out_v : T
where T: Sized,

requires
self.id() === old(perm).id(),
ensures
perm.id() === old(perm).id(),
*perm.value() === in_v,
out_v === *old(perm).value(),
Source

pub exec fn into_inner(self, Tracked(perm): Tracked<PointsTo<T>>) -> v : T
where T: Sized,

requires
self.id() === perm.id(),
ensures
v === *perm.value(),
Source

pub exec fn write(&self, Tracked(perm): Tracked<&mut PointsTo<T>>, in_v: T)
where T: Sized,

requires
self.id() === old(perm).id(),
ensures
perm.id() === old(perm).id(),
*perm.value() === in_v,
Source

pub exec fn read(&self, Tracked(perm): Tracked<&PointsTo<T>>) -> out_v : T
where T: Copy + Sized,

requires
self.id() === perm.id(),

Trait Implementations§

Source§

impl<T: ?Sized> Send for PCell<T>

Source§

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>
where T: Unpin + ?Sized,

§

impl<T> UnwindSafe for PCell<T>
where T: UnwindSafe + ?Sized,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, VERUS_SPEC__A> FromSpec<T> for VERUS_SPEC__A
where VERUS_SPEC__A: From<T>,

Source§

exec fn obeys_from_spec() -> bool

Source§

exec fn from_spec(v: T) -> VERUS_SPEC__A

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, VERUS_SPEC__A> IntoSpec<T> for VERUS_SPEC__A
where VERUS_SPEC__A: Into<T>,

Source§

exec fn obeys_into_spec() -> bool

Source§

exec fn into_spec(self) -> T

Source§

impl<T, U> IntoSpecImpl<U> for T
where U: From<T>,

Source§

open spec fn obeys_into_spec() -> bool

{ <U as FromSpec<Self>>::obeys_from_spec() }
Source§

open spec fn into_spec(self) -> U

{ U::from_spec(self) }
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, VERUS_SPEC__A> TryFromSpec<T> for VERUS_SPEC__A
where VERUS_SPEC__A: TryFrom<T>,

Source§

exec fn obeys_try_from_spec() -> bool

Source§

exec fn try_from_spec( v: T, ) -> Result<VERUS_SPEC__A, <VERUS_SPEC__A as TryFrom<T>>::Error>

Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T, VERUS_SPEC__A> TryIntoSpec<T> for VERUS_SPEC__A
where VERUS_SPEC__A: TryInto<T>,

Source§

exec fn obeys_try_into_spec() -> bool

Source§

exec fn try_into_spec(self) -> Result<T, <VERUS_SPEC__A as TryInto<T>>::Error>

Source§

impl<T, U> TryIntoSpecImpl<U> for T
where U: TryFrom<T>,

Source§

open spec fn obeys_try_into_spec() -> bool

{ <U as TryFromSpec<Self>>::obeys_try_from_spec() }
Source§

open spec fn try_into_spec(self) -> Result<U, <U as TryFrom<T>>::Error>

{ <U as TryFromSpec<Self>>::try_from_spec(self) }