updated generator and pinning chapter

This commit is contained in:
cfsamson
2020-01-28 17:58:12 +01:00
parent 4249cacb91
commit f6e0983721
3 changed files with 110 additions and 3 deletions

View File

@@ -5,7 +5,7 @@ cache:
- cargo - cargo
rust: rust:
- stable - nightly
before_script: before_script:
- (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update)

View File

@@ -349,9 +349,114 @@ impl Generator for GeneratorA {
``` ```
But now, let's But now, let's prevent the segfault from happening using `Pin`:
```rust ```rust, nightly
#![feature(optin_builtin_traits)]
pub fn test2() {
let mut gen = GeneratorA::start();
let mut gen2 = GeneratorA::start();
std::mem::swap(&mut gen, &mut gen2);
// constructing a `Pin::new()` on a type which does not implement `Unpin` is unsafe.
// However, as I mentioned in the start a Boxed type automatically implements `Unpin`
// so to stay in safe Rust we can use that to avoid unsafe. You can also use crates
// like `pin_utils` to do this safely, just remember that they use unsafe under the hood
// so it's like using an already-reviewed unsafe implementation.
let mut boxed_gen = Box::pin(gen);
if let GeneratorState::Yielded(n) = boxed_gen.as_mut().resume() {
println!("Got value {}", n);
}
let mut boxed_gen2 = Box::pin(gen2);
if let GeneratorState::Yielded(n) = boxed_gen2.as_mut().resume() {
println!("Gen2 got value {}", n);
};
// This won't work
// std::mem::swap(&mut gen, &mut gen2);
// This will work but will just swap the pointers. Nothing inherently bad happens here.
// std::mem::swap(&mut boxed_gen, &mut boxed_gen2);
let _ = boxed_gen.as_mut().resume();
let _ = boxed_gen2.as_mut().resume();
}
use std::ptr::NonNull;
use std::pin::Pin;
// If you've ever wondered why the parameters are called Y and R the naming from
// the original rfc most likely holds the answer
enum GeneratorState<Y, R> {
// originally called `CoResult`
Yielded(Y), // originally called `Yield(Y)`
Complete(R), // originally called `Return(R)`
}
trait Generator {
type Yield;
type Return;
fn resume(mut self: Pin<&mut Self>) -> GeneratorState<Self::Yield, Self::Return>;
}
enum GeneratorA {
Enter,
Yield1 {
to_borrow: String,
borrowed: *const String, // Normally you'll see `std::ptr::NonNull` used instead of *ptr
},
Exit,
}
impl GeneratorA {
fn start() -> Self {
GeneratorA::Enter
}
}
// This tells us that the underlying pointer is not safe to move after pinning. In this case,
// only we as implementors "feel" this, however, if someone is relying on our Pinned pointer
// this will prevent them from moving it. You need to enable the feature flag
// `#![feature(optin_builtin_traits)]` and use the nightly compiler to implement `!Unpin`.
impl !Unpin for GeneratorA { }
impl Generator for GeneratorA {
type Yield = usize;
type Return = ();
fn resume(mut self: Pin<&mut Self>) -> GeneratorState<Self::Yield, Self::Return> {
// lets us get ownership over current state
let this = unsafe { self.get_unchecked_mut() };
match this {
GeneratorA::Enter => {
let to_borrow = String::from("Hello");
let borrowed = &to_borrow;
let res = borrowed.len();
// Tricks to actually get a self reference
*this = GeneratorA::Yield1 {to_borrow, borrowed: std::ptr::null()};
match this {
GeneratorA::Yield1{to_borrow, borrowed} => *borrowed = to_borrow,
_ => ()
};
GeneratorState::Yielded(res)
}
GeneratorA::Yield1 {borrowed, ..} => {
let borrowed: &String = unsafe {&**borrowed};
println!("{} world", borrowed);
*this = GeneratorA::Exit;
GeneratorState::Complete(())
}
GeneratorA::Exit => panic!("Can't advance an exited generator!"),
}
}
}
``` ```
However, this is also the point where we need to talk about one more concept to However, this is also the point where we need to talk about one more concept to

View File

@@ -2,6 +2,7 @@
```rust ```rust
#![feature(duration_float)]
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::mpsc::{channel, Sender}; use std::sync::mpsc::{channel, Sender};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@@ -134,6 +135,7 @@ impl Drop for Reactor {
``` ```
```rust ```rust
#![feature(duration_float)]
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::mpsc::{channel, Sender}; use std::sync::mpsc::{channel, Sender};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};