Expand description
This tests the QCell implementation without the alloc feature.
You should not be able to use QCellOwnerPinned without pinning it first
let mut owner1 = QCellOwnerPinned::new();
let id = owner1.id();QCellOwnerPinned should be !Unpin
fn is_unpin<T: Unpin>() {}
is_unpin::<QCellOwnerPinned>();It should be impossible to copy a QCellOwnerPinned:
let mut owner1 = QCellOwnerPinned::new();
let mut owner2 = owner1;
pin_mut!(owner1); // Compile fail
let rc = Rc::new(owner1.as_ref().cell(100u32));Including after it was pinned:
let mut owner1 = QCellOwnerPinned::new();
pin_mut!(owner1);
let mut owner2 = owner1;
let rc = Rc::new(owner1.as_ref().cell(100u32)); // Compile failIt should be impossible to clone a QCellOwnerPinned:
let mut owner1 = QCellOwnerPinned::new();
let owner2 = owner1.clone(); // Compile failIncluding after it was pinned:
let mut owner1 = QCellOwnerPinned::new();
pin_mut!(owner1);
let owner2 = owner1.clone(); // Compile failTwo different owners can’t borrow each other’s cells immutably:
let mut owner1 = QCellOwnerPinned::new();
let mut owner2 = QCellOwnerPinned::new();
pin_mut!(owner1);
pin_mut!(owner2);
let c1 = Rc::new(owner1.as_ref().cell(100u32));
let c1ref = owner2.as_ref().ro(&c1); // Panics here
println!("{}", *c1ref);Or mutably:
let mut owner1 = QCellOwnerPinned::new();
let mut owner2 = QCellOwnerPinned::new();
pin_mut!(owner1);
pin_mut!(owner2);
let c1 = Rc::new(owner1.as_ref().cell(100u32));
let c1mutref = owner2.as_mut().rw(&c1); // Panics here
println!("{}", *c1mutref);You can’t have two separate mutable borrows active on the same owner at the same time:
let mut owner = QCellOwnerPinned::new();
pin_mut!(owner);
let c1 = Rc::new(owner.as_ref().cell(100u32));
let c2 = Rc::new(owner.as_ref().cell(200u32));
let c1mutref = owner.as_mut().rw(&c1);
let c2mutref= owner.as_mut().rw(&c2); // Compile error
*c1mutref += 1;
*c2mutref += 2;However with rw2() you can do two mutable borrows at the
same time, since this call checks at runtime that the two
references don’t refer to the same memory:
let (c1mutref, c2mutref) = owner.as_mut().rw2(&c1, &c2);
*c1mutref += 1;
*c2mutref += 2;
assert_eq!(303, owner.as_ref().ro(&c1) + owner.as_ref().ro(&c2)); // Success!You can’t have a mutable borrow at the same time as an immutable borrow:
let c1ref = owner.as_ref().ro(&c1);
let c1mutref = owner.as_mut().rw(&c1); // Compile error
println!("{}", *c1ref);Not even if it’s borrowing a different object:
let c1mutref = owner.as_mut().rw(&c1);
let c2ref = owner.as_ref().ro(&c2); // Compile error
*c1mutref += 1;Many immutable borrows at the same time is fine:
let c1ref = owner.as_ref().ro(&c1);
let c2ref = owner.as_ref().ro(&c2);
let c1ref2 = owner.as_ref().ro(&c1);
let c2ref2 = owner.as_ref().ro(&c2);
assert_eq!(600, *c1ref + *c2ref + *c1ref2 + *c2ref2); // Success!Whilst a reference is active, it’s impossible to drop the Rc:
let c1ref = owner.as_ref().ro(&c1);
drop(c1); // Compile error
println!("{}", *c1ref);Also, whilst a reference is active, it’s impossible to call
anything else that uses the owner in an incompatible way,
e.g. &mut when there’s a & reference:
fn test(o: Pin<&mut QCellOwnerPinned>) {}
let c1ref = owner.as_ref().ro(&c1);
test(owner.as_mut()); // Compile error
println!("{}", *c1ref);Or & when there’s a &mut reference:
fn test(o: Pin<&QCellOwnerPinned>) {}
let c1mutref = owner.as_mut().rw(&c1);
test(owner.as_ref()); // Compile error
*c1mutref += 1;QCellOwnerPinned and QCell should be both
Send and Sync by default:
fn is_send_sync<T: Send + Sync>() {}
is_send_sync::<QCellOwnerPinned>();
is_send_sync::<QCell<()>>();So for example we can share a cell ref between threads (Sync), and pass an owner back and forth (Send):
let mut owner = QCellOwnerPinned::new();
pin_mut!(owner);
let cell = owner.as_ref().cell(100_i32);
*owner.as_mut().rw(&cell) += 1;
let cell_ref = &cell;
let mut owner = crossbeam::scope(move |s| {
s.spawn(move |_| {
*owner.as_mut().rw(cell_ref) += 2;
owner
}).join().unwrap()
}).unwrap();
*owner.as_mut().rw(&cell) += 4;
assert_eq!(*owner.as_ref().ro(&cell), 107);However you can’t send a cell that’s still borrowed:
let mut owner = Box::pin(QCellOwnerPinned::new());
let cell = owner.as_ref().cell(100);
let val_ref = owner.as_ref().ro(&cell);
std::thread::spawn(move || {
assert_eq!(*owner.as_ref().ro(&cell), 100);
}).join();
assert_eq!(*val_ref, 100);If the contained type isn’t Sync, though, then QCell shouldn’t
be Sync either:
fn is_sync<T: Sync>() {}
is_sync::<QCell<Cell<i32>>>(); // Compile failIf the contained type isn’t Send, the QCell should be neither
Sync nor Send:
fn is_sync<T: Sync>() {}
is_sync::<QCell<Rc<()>>>(); // Compile failfn is_send<T: Send>() {}
is_send::<QCell<Rc<()>>>(); // Compile faillet mut owner = Box::pin(QCellOwnerPinned::new());
let cell = owner.as_ref().cell(Rc::new(100));
// We aren't permitted to move the Rc to another thread
std::thread::spawn(move || { // Compile fail
assert_eq!(100, **owner.as_ref().ro(&cell));
}).join();