Last active
November 6, 2024 09:44
-
-
Save sandersaares/3545dad263f50db1232c8a8728bac432 to your computer and use it in GitHub Desktop.
pins-in-rust
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// BAD CODE: This example is intentionally wrong. | |
pub struct BagOfApples { | |
count: usize, | |
// In the example code, this self-reference is mostly useless. | |
// This is just to keep the example code simple - the emphasis is | |
// on the effects of pinning, not why a type may be designed to need it. | |
self_reference: *mut BagOfApples, | |
} | |
impl BagOfApples { | |
pub fn new() -> Self { | |
BagOfApples { | |
count: 0, | |
// We cannot set this here because we have not yet | |
// created the BagOfApples - there is nothing to reference. | |
self_reference: ptr::null_mut(), | |
} | |
} | |
/// Call this after creating a BagOfApples to initialize the instance. | |
pub fn initialize(&mut self) { | |
self.self_reference = self; | |
} | |
pub fn count(&self) -> usize { | |
assert!( | |
!self.self_reference.is_null(), | |
"BagOfApples is not initialized" | |
); | |
// SAFETY: Simple read-only access to the count field, which | |
// is safe. We do it via the pointer for example purposes. | |
unsafe { (*self.self_reference).count } | |
} | |
} | |
// BAD CODE: This example is intentionally wrong. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let mut bag = BagOfApples::new(); | |
bag.initialize(); | |
println!("Apple count: {}", bag.count()); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// BAD CODE: This example is intentionally wrong. | |
let mut bag = BagOfApples::new(); | |
bag.initialize(); | |
// We move the bag into a box, which is a different memory location. | |
// Invalid code: it is not legal to move the bag after creation because it is | |
// self-referential. BagOfApples is an unsound type because it allows this. | |
let boxed_bag = Box::new(bag); | |
// This could result in invalid memory access, causing undefined behavior. | |
// It may (appear to) work depending on circumstances and what compiler | |
// optimizations are applied in specific situations but this does not make it valid. | |
println!("Apple count: {}", boxed_bag.count()); | |
// BAD CODE: This example is intentionally wrong. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pub struct BagOfApples { | |
count: usize, | |
// In the example code, this self-reference is mostly useless. | |
// This is just to keep the example code simple - the emphasis is | |
// on the effects of pinning, not why a type may be designed to need it. | |
self_reference: *mut BagOfApples, | |
_require_pin: std::marker::PhantomPinned, | |
} | |
pub fn new() -> Self { | |
BagOfApples { | |
count: 0, | |
// We cannot set this here because we have not yet | |
// created the BagOfApples - there is nothing to reference. | |
self_reference: ptr::null_mut(), | |
_require_pin: std::marker::PhantomPinned, | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// Call this after creating a BagOfApples to initialize the instance. | |
pub fn initialize(mut self: Pin<&mut Self>) { | |
// SAFETY: BagOfApples requires pinning and we do not allow | |
// the obtained pointer to be exposed outside this type, so | |
// we know it always points to a valid value and therefore it | |
// is safe to store the pointer. We have also reviewed the code of | |
// this function to ensure that we do not move the BagOfApples | |
// instance via the reference we obtain from here nor via the pointer. | |
let self_mut = unsafe { self.as_mut().get_unchecked_mut() }; | |
self_mut.self_reference = self_mut; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let mut bag = BagOfApples::new(); | |
bag.initialize(); | |
// ^ ERROR | |
// no method named `initialize` found for struct `better::BagOfApples` in the current scope | |
// method not found in `BagOfApples` | |
// lib.rs(66, 5): method `initialize` not found for this struct | |
// lib.rs(114, 9): consider pinning the expression: `let mut pinned = std::pin::pin!(`, `); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let mut bag = Box::pin(BagOfApples::new()); | |
bag.as_mut().initialize(); | |
println!("Apple count: {}", bag.count()); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pub fn count(&self) -> usize { | |
assert!( | |
!self.self_reference.is_null(), | |
"BagOfApples is not initialized" | |
); | |
// SAFETY: Simple read-only access to the count field, which | |
// is safe. We do it via the pointer for example purposes. | |
unsafe { (*self.self_reference).count } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pub struct BagOfApples { | |
count: usize, | |
capacity: usize, | |
// ... | |
} | |
impl BagOfApples { | |
// ... | |
pub fn set_capacity(&mut self, capacity: usize) { | |
self.capacity = capacity; | |
} | |
// ... | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let mut bag = Box::pin(BagOfApples::new()); | |
bag.as_mut().initialize(); | |
// error[E0596]: cannot borrow data in dereference of `Pin<Box<BagOfApples>>` as mutable | |
bag.set_capacity(123); | |
// error[E0596]: cannot borrow data in dereference of `Pin<&mut BagOfApples>` as mutable | |
bag.as_mut().set_capacity(123); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let mut bag = BagOfApples::new(); | |
bag.set_capacity(123); | |
let mut bag = Box::pin(bag); | |
bag.as_mut().initialize(); | |
println!("Apple count: {}", bag.count()); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pub trait BagOfFruit { | |
fn set_capacity(self: Pin<&mut Self>, capacity: usize); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pub struct BagOfBananas { | |
capacity: usize, | |
_require_pin: std::marker::PhantomPinned, | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
impl BagOfFruit for BagOfBananas { | |
fn set_capacity(mut self: Pin<&mut Self>, capacity: usize) { | |
// SAFETY: BagOfBananas requires pinning. We have reviewed the code | |
// in this function to ensure that we do not move the BagOfBananas | |
// instance via the reference we obtain from here. | |
let self_mut: &mut BagOfBananas = unsafe { | |
self.as_mut().get_unchecked_mut() | |
}; | |
self_mut.capacity = capacity; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let pinned = Box::pin(42); | |
println!("Pinned integer: {}", *pinned); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pub struct BagOfOranges { | |
capacity: usize, | |
} | |
impl BagOfFruit for BagOfOranges { | |
fn set_capacity(mut self: Pin<&mut Self>, capacity: usize) { | |
// This type does not require pinning, so we can simply dereference. | |
let self_mut: &mut BagOfOranges = &mut self; | |
self_mut.capacity = capacity; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct FruitStand { | |
apples: BagOfApples, | |
oranges: BagOfOranges, | |
bananas: BagOfBananas, | |
total_sold: usize, | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct FruitStand { | |
apples: Pin<Box<BagOfApples>>, | |
oranges: Pin<Box<BagOfOranges>>, | |
bananas: Pin<Box<BagOfBananas>>, | |
total_sold: usize, | |
} | |
impl FruitStand { | |
fn new() -> Self { | |
FruitStand { | |
apples: Box::pin(BagOfApples::default()), | |
oranges: Box::pin(BagOfOranges::default()), | |
bananas: Box::pin(BagOfBananas::default()), | |
total_sold: 0, | |
} | |
} | |
fn sell_one_of_each(&mut self) { | |
self.apples.as_mut().sell_one(); | |
self.oranges.as_mut().sell_one(); | |
self.bananas.as_mut().sell_one(); | |
self.total_sold += 3; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#[derive(Default)] | |
struct FruitStand { | |
apples: BagOfApples, | |
oranges: BagOfOranges, | |
bananas: BagOfBananas, | |
total_sold: usize, | |
} | |
impl FruitStand { | |
fn sell_one_of_each(self: Pin<&mut Self>) { | |
self.apples.sell_one(); | |
// error[E0599]: no method named `sell_one` found for struct `BagOfApples` in the current scope | |
// --> examples\05_project.rs:85:21 | |
// | | |
// 4 | struct BagOfApples { | |
// | ------------------ method `sell_one` not found for this struct | |
// ... | |
// 85 | self.apples.sell_one(); | |
// | ^^^^^^^^ method not found in `BagOfApples` | |
// | | |
// help: consider pinning the expression | |
// | | |
// 85 ~ let mut pinned = std::pin::pin!(self.apples); | |
// 86 ~ pinned.as_mut().sell_one(); | |
// | | |
self.oranges.sell_one(); | |
self.bananas.sell_one(); | |
self.total_sold += 3; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#[derive(Default)] | |
#[pin_project] | |
struct FruitStand { | |
#[pin] | |
apples: BagOfApples, | |
#[pin] | |
oranges: BagOfOranges, | |
#[pin] | |
bananas: BagOfBananas, | |
total_sold: usize, | |
} | |
impl FruitStand { | |
fn sell_one_of_each(mut self: Pin<&mut Self>) { | |
let self_projected = self.as_mut().project(); | |
self_projected.apples.sell_one(); | |
self_projected.oranges.sell_one(); | |
self_projected.bananas.sell_one(); | |
*self_projected.total_sold += 3; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment