Created
February 18, 2020 21:33
-
-
Save cynecx/8f1abf1729468c73b7192108b26ff612 to your computer and use it in GitHub Desktop.
Quasi self-referential generator
This file contains hidden or 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
| // In the future when Rust supports Rust#69268, | |
| // it might be possible to write 100% safe self-referencial structs with the help of generators. | |
| #![feature(generic_associated_types)] | |
| #![allow(incomplete_features)] | |
| use core::marker::{PhantomData, PhantomPinned}; | |
| use core::pin::Pin; | |
| use core::ptr; | |
| pub enum GeneratorState<Y, R> { | |
| Yielded(Y), | |
| Complete(R), | |
| } | |
| pub trait Generator<R> { | |
| type Yield<'a>; | |
| type Return; | |
| fn resume<'a, 'b>( | |
| self: Pin<&'a mut Self>, | |
| arg: R, | |
| ) -> GeneratorState<Self::Yield<'a>, Self::Return>; | |
| } | |
| struct TestGen { | |
| owned: String, | |
| cursor: (*const u8, usize), | |
| _pinned: PhantomPinned, | |
| } | |
| impl TestGen { | |
| fn new(owned: String) -> Pin<Box<Self>> { | |
| Box::pin(TestGen { | |
| owned, | |
| cursor: (ptr::null(), 0), | |
| _pinned: PhantomPinned, | |
| }) | |
| } | |
| } | |
| enum Action { | |
| Renew(fn(&mut String) -> &str), | |
| Borrow, | |
| } | |
| impl Generator<Action> for TestGen { | |
| type Yield<'a> = &'a str; | |
| type Return = (); | |
| fn resume<'a>( | |
| self: Pin<&'a mut Self>, | |
| arg: Action, | |
| ) -> GeneratorState<Self::Yield<'a>, Self::Return> { | |
| let mut arg = arg; | |
| let g = unsafe { Pin::get_unchecked_mut(self) }; | |
| loop { | |
| match arg { | |
| Action::Borrow => { | |
| assert!(!g.cursor.0.is_null()); | |
| let slice = { | |
| let p = ptr::slice_from_raw_parts(g.cursor.0, g.cursor.1); | |
| unsafe { core::str::from_utf8_unchecked(&*p) } | |
| }; | |
| return GeneratorState::Yielded(slice); | |
| } | |
| Action::Renew(renew) => { | |
| let r = renew(&mut g.owned); | |
| g.cursor = (r.as_ptr(), r.len()); | |
| arg = Action::Borrow; | |
| }, | |
| } | |
| } | |
| } | |
| } | |
| fn main() { | |
| let mut gen = TestGen::new("Hello World!".to_owned()); | |
| let msg = gen.as_mut().resume(Action::Renew(|x: &mut String| &x[6..])); | |
| if let GeneratorState::Yielded(msg) = &msg { | |
| println!("{}", msg); | |
| } | |
| let msg = gen.as_mut().resume(Action::Renew(|x: &mut String| &x[..])); | |
| if let GeneratorState::Yielded(msg) = &msg { | |
| println!("{}", msg); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment