pub struct QCellOwnerPinned { /* private fields */ }
Expand description
Borrowing-owner of zero or more QCell
instances, based on a
pinned struct
This type uses its own memory address to provide a unique owner
ID, which requires no allocations and only 2 bytes of storage. So
this is suitable for a no_std
environment without an allocator.
The owner can be created on the stack, or on the heap, as
required. To ensure its memory address cannot change while cells
exist that are owned by it, it requires itself to be pinned before
any operation interacting with the ID is attempted.
There are many ways to safely pin a value, such as
Box::pin
,
pin-utils::pin_mut!
,
tokio::pin!
,
or the pin-project
crate.
The following example uses the pin_mut!
macro from the
pin-utils
crate:
use pin_utils::pin_mut;
use qcell::{QCell, QCellOwnerPinned};
let mut owner = QCellOwnerPinned::new();
pin_mut!(owner);
let item = Rc::new(owner.as_ref().cell(Vec::<u8>::new()));
owner.as_mut().rw(&item).push(1);
test(owner, &item);
fn test(owner: Pin<&mut QCellOwnerPinned>, item: &Rc<QCell<Vec<u8>>>) {
owner.rw(&item).push(2);
}
This example incorporates the QCellOwnerPinned
into a larger
structure kept on the stack, and accesses it using the
pin-project
crate:
use crate::qcell::{QCell, QCellOwnerPinned};
use pin_project::pin_project;
use pin_utils::pin_mut;
#[pin_project]
struct MyStruct {
_misc: usize, // Unpinned value
#[pin]
owner: QCellOwnerPinned,
}
let mystruct = MyStruct {
_misc: 0,
owner: QCellOwnerPinned::new(),
};
pin_mut!(mystruct);
let item = Rc::new(
mystruct.as_mut().project().owner.as_ref().cell(Vec::<u8>::new())
);
mystruct.as_mut().project().owner.rw(&item).push(1);
test(mystruct.as_mut().project().owner, &item);
fn test(owner: Pin<&mut QCellOwnerPinned>, item: &Rc<QCell<Vec<u8>>>) {
owner.rw(&item).push(2);
}
§Safety
After the owner is pinned, its address is used as a temporally unique ID. This detects use of the wrong owner to access a cell at runtime, which is a programming error.
Note that even without Pin
, this would still be sound, because
there would still be only one owner valid at any one time with the
same ID, because two owners cannot occupy the same memory.
However Pin
is useful because it helps the coder avoid
accidentally moving an owner from one address to another without
realizing it, and causing panics due to the changed owner ID.
The ID generated from this type cannot clash with IDs generated by
QCellOwner
(which is also based on the addresses of occupied
memory, but always on the heap), or QCellOwnerSeq
(which only
allocates odd IDs, which cannot clash with addresses from this
type which always have an alignment of 2). So this should
successfully defend against all malicious and unsafe use. If not,
please raise an issue.
The same unique ID may later be allocated to someone else once you drop the returned owner, but this cannot be abused to cause unsafe access to cells because there will still be only one owner active at any one time with that ID. Also it cannot be used maliciously to access cells which don’t belong to the new caller, because you also need a reference to the cells. So for example if you have a graph of cells that is only accessible through a private structure, then someone else getting the same owner ID makes no difference, because they have no way to get a reference to those cells. In any case, you are probably going to drop all those cells at the same time as dropping the owner, because they are no longer of any use without the owner ID.
Implementations§
Source§impl QCellOwnerPinned
impl QCellOwnerPinned
Sourcepub fn id(self: Pin<&Self>) -> QCellOwnerID
pub fn id(self: Pin<&Self>) -> QCellOwnerID
Get the internal owner ID. This may be used to create
QCell
instances without needing a borrow on this
structure, which is useful if this structure is already
borrowed.
Requires this owner to be pinned before use.
Sourcepub fn cell<T>(self: Pin<&Self>, value: T) -> QCell<T>
pub fn cell<T>(self: Pin<&Self>, value: T) -> QCell<T>
Create a new cell owned by this owner instance.
Requires this owner to be pinned before use.
Sourcepub fn ro<'a, T: ?Sized>(self: Pin<&'a Self>, qc: &'a QCell<T>) -> &'a T
pub fn ro<'a, T: ?Sized>(self: Pin<&'a Self>, qc: &'a QCell<T>) -> &'a T
Borrow contents of a QCell
immutably (read-only). Many
QCell
instances can be borrowed immutably at the same time
from the same owner. Panics if the QCell
is not owned by
this QCellOwnerPinned
.
Requires this owner to be pinned before use.
Sourcepub fn rw<'a, T: ?Sized>(self: Pin<&'a mut Self>, qc: &'a QCell<T>) -> &'a mut T
pub fn rw<'a, T: ?Sized>(self: Pin<&'a mut Self>, qc: &'a QCell<T>) -> &'a mut T
Borrow contents of a QCell
mutably (read-write). Only one
QCell
at a time can be borrowed from the owner using this
call. The returned reference must go out of scope before
another can be borrowed. Panics if the QCell
is not owned
by this QCellOwnerPinned
.
Requires this owner to be pinned before use.
Sourcepub fn rw2<'a, T: ?Sized, U: ?Sized>(
self: Pin<&'a mut Self>,
qc1: &'a QCell<T>,
qc2: &'a QCell<U>,
) -> (&'a mut T, &'a mut U)
pub fn rw2<'a, T: ?Sized, U: ?Sized>( self: Pin<&'a mut Self>, qc1: &'a QCell<T>, qc2: &'a QCell<U>, ) -> (&'a mut T, &'a mut U)
Borrow contents of two QCell
instances mutably. Panics if
the two QCell
instances point to the same memory. Panics
if either QCell
is not owned by this QCellOwnerPinned
.
Requires this owner to be pinned before use.
Sourcepub fn rw3<'a, T: ?Sized, U: ?Sized, V: ?Sized>(
self: Pin<&'a mut Self>,
qc1: &'a QCell<T>,
qc2: &'a QCell<U>,
qc3: &'a QCell<V>,
) -> (&'a mut T, &'a mut U, &'a mut V)
pub fn rw3<'a, T: ?Sized, U: ?Sized, V: ?Sized>( self: Pin<&'a mut Self>, qc1: &'a QCell<T>, qc2: &'a QCell<U>, qc3: &'a QCell<V>, ) -> (&'a mut T, &'a mut U, &'a mut V)
Borrow contents of three QCell
instances mutably. Panics
if any pair of QCell
instances point to the same memory.
Panics if any QCell
is not owned by this
QCellOwnerPinned
.
Requires this owner to be pinned before use.