Skip to content

Instantly share code, notes, and snippets.

@rust-play
Created August 7, 2020 10:54
Show Gist options
  • Save rust-play/041f9a6f5dfa3a5fc59ece7105cc4dfd to your computer and use it in GitHub Desktop.
Save rust-play/041f9a6f5dfa3a5fc59ece7105cc4dfd to your computer and use it in GitHub Desktop.
Code shared from the Rust Playground
// WARNING: THIS CODE IS A HACKY PROTOTYPE FOR TOY PURPOSES ONLY.
// CHOOSING TO COPY THIS CODE INTO YOUR RUST PROGRAM IS WILDLY UNSOUND.
//
// ping me (davidhewitt) on the PyO3 issue tracker and we can figure out
// how to do this safely.
use std::collections::{HashMap, hash_map::Iter as MapIter};
// pyclass 1
pub struct Class {
data: HashMap<String, String>
}
// pyclass 2
pub struct Iterator {
// Ouch this type signature is horrible! Maybe PyO3 can pack this up in
// a type alias / with a macro.
inner: Box<dyn for<'a> PyOwned<MapIter<'a, String, String>>>
}
// pyproto for Iterator
impl Iterator {
fn __next__(&mut self) -> Option<(&String, &String)> {
self.inner.get_mut().next()
}
}
// Not actually how PyCell / PyRef implemented, but it'll do for sketching
// the types here.
pub struct PyCell<T>(T);
pub struct PyRef<'a, T>(&'a T);
impl<T> PyCell<T> {
fn borrow(&self) -> PyRef<T> {
PyRef(&self.0)
}
}
trait PyOwned<T> {
fn get_mut(&mut self) -> &mut T;
}
impl<'py> PyRef<'py, Class> {
fn to_dyn_owned(self: &Self, it: MapIter<'py, String, String>) -> Box<dyn for<'a> PyOwned<MapIter<'a, String, String>>>
{
// In here be wildly unsafe code to make the type system line up.
// This should be able to be "swept under the rug" by PyO3 in a safe wrapper.
// This is the wildly unsafe escape from Rust's lifetime system
let static_r = unsafe { std::mem::transmute(self) };
let static_it = unsafe { std::mem::transmute(it) };
struct PyOwnedImpl {
// FIXME: this is totally wrong, needs to be Py<T> or similar which stores
// the python pointer rather than the borrow. As-is, this would be pointing
// at dangling memory.
_r: PyRef<'static, Class>,
// This 'static also smells bad, but it's the only way to avoid having
// a lifetime on the outer value.
// It's probably needed to convince Rust that the MapIter lives
// long enough.
inner: MapIter<'static, String, String>
}
impl<'a> PyOwned<MapIter<'a, String, String>> for PyOwnedImpl {
fn get_mut(&mut self) -> &mut MapIter<'a, String, String> {
// Without this transmute, the compiler cannot assert that 'a outlives static... !?
unsafe { std::mem::transmute(&mut self.inner) }
}
}
Box::new(PyOwnedImpl { _r: static_r, inner: static_it })
}
}
fn main() {
let mut data = HashMap::new();
data.insert("foo".to_string(), "bar".to_string());
let cell = PyCell(Class { data });
let r = cell.borrow();
let mut it = Iterator {
inner: PyRef::to_dyn_owned(&r, r.0.data.iter())
};
println!("{:?}", it.__next__());
assert_eq!(it.__next__(), None);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment