Created
December 18, 2019 18:13
-
-
Save pchampin/7fbf3262ab22b6a0f6a6eae8a6aa938c to your computer and use it in GitHub Desktop.
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
//! This is an experiment on how to get rid of lifetime parameters in | |
//! Sopghia's Graph and Dataset traits. It demonstrates the general idea | |
//! on a simplified version of Triple and Graph. | |
//! | |
//! Graph iterators no longer return Graph::Triple's, | |
//! they return a safe abstraction around it: GuardedTRiple<'a, Graph::Triple>. | |
//! | |
//! Graph::Triple itself can be one of several options: | |
//! * T (where T implements Triple) | |
//! * *const T (where T implements Triple) | |
//! * [*const T; 3] (where T is Term<X>) | |
//! | |
//! Intuitively: | |
//! * the first option must be used when triples are created during iteration, | |
//! and their ownership is transferred to the caller; | |
//! * the second option must be used when triples are owned by the Graph, | |
//! and the caller borrows them for the lifetime of the iterator; | |
//! * the third option must be used when the Graph owns its terms, | |
//! but does not own triples per se. | |
//! | |
//! The triples() method (and all its sister methods) | |
//! must use one of the methods provided by the Graph interface | |
//! to produce GuardedTriple: | |
//! * guard_triple for the first option | |
//! * guard_triple_ref for the second option | |
//! * guard_term_refs for the second option | |
//! These methods guarantee that the inner pointers will never be used | |
//! unsafely. | |
//! | |
//! NB: technically, the three option above are the three provided implementation | |
//! of the UnsafeTriple trait, which should not be used directly. | |
//! In the future, more options could be provided by adding more implementations, | |
//! but users of the Sophia crate are not expected to do that. | |
use std::marker::PhantomData; | |
// **** Dummy Triple trait | |
pub trait Triple { | |
type Term: AsRef<str> + ?Sized; | |
fn s(&self) -> &Self::Term; | |
fn p(&self) -> &Self::Term { | |
self.s() // just for faster prototyping | |
} | |
fn o(&self) -> &Self::Term { | |
self.s() // just for faster prototyping | |
} | |
} | |
/// **** Dummy Triple implementation | |
impl Triple for String { | |
type Term = str; | |
fn s(&self) -> &Self::Term { | |
&self | |
} | |
// TODO implement p() and o() | |
} | |
// UnsafeTriple trait | |
/// This trait should not be used directly. | |
/// It is part of the mechanics that allow `GIterstor`s to work. | |
pub trait UnsafeTriple { | |
type Term: AsRef<str> + ?Sized; | |
#[inline] | |
unsafe fn us(&self) -> &Self::Term; | |
#[inline] | |
unsafe fn up(&self) -> &Self::Term { | |
self.us() // just for faster prototyping | |
} | |
#[inline] | |
unsafe fn uo(&self) -> &Self::Term { | |
self.us() // just for faster prototyping | |
} | |
} | |
impl<T: Triple> UnsafeTriple for T { | |
type Term = T::Term; | |
unsafe fn us(&self) -> &Self::Term { | |
self.s() | |
} | |
// TODO implement p() and o() | |
} | |
impl<T: Triple> UnsafeTriple for *const T { | |
type Term = T::Term; | |
unsafe fn us(&self) -> &Self::Term { | |
(**self).s() | |
} | |
// TODO implement p() and o() | |
} | |
impl<T: AsRef<str> + ?Sized> UnsafeTriple for [*const T;3] { | |
type Term = T; | |
unsafe fn us(&self) -> &Self::Term { | |
&*self[0] | |
} | |
} | |
/// Safe abstraction used by `Graphs`'s iterators. | |
/// | |
/// See | |
/// * Graph.guard_triple | |
/// * Graph.guard_triple_ref | |
/// * Graph.guard_term_refs | |
/// | |
#[derive(Debug)] | |
pub struct GuardedTriple<'a, T: UnsafeTriple> { | |
_phantom: PhantomData<&'a T>, | |
triple: T, | |
} | |
impl<'a, T: UnsafeTriple> Triple for GuardedTriple<'a, T> { | |
type Term = T::Term; | |
fn s(&self) -> &Self::Term { | |
unsafe { self.triple.us() } | |
} | |
// TODO implement p() and o() | |
} | |
// Dummy Graph trait, with the minimally required structure | |
/// Type alias for all iterators returned by `Graph` methods. | |
type GIterator<'a, G> = Box<dyn Iterator<Item=GuardedTriple<'a, <G as Graph>::Triple>> + 'a>; | |
pub trait Graph { | |
/// This type is the one used *indternally* by `GIterator`s. | |
/// What users will get is a safe wrapper around this type. | |
/// This should be one of the 3 options documented at the top of this file. | |
type Triple: UnsafeTriple; | |
fn triples<'s>(&'s self) -> GIterator<'s, Self>; | |
/// To be used in iterators, when Self::Triple is T | |
fn guard_triple<'s>(&'s self, triple: Self::Triple) -> GuardedTriple<'s, Self::Triple>where | |
Self::Triple: Triple, | |
{ | |
GuardedTriple { | |
_phantom: PhantomData, | |
triple, | |
} | |
} | |
/// To be used in iterators, when Self::Triple is *const T | |
fn guard_triple_ref<'s, T>(&'s self, triple: &'s T) -> GuardedTriple<'s, Self::Triple>where | |
T: Triple, | |
Self: Graph<Triple = *const T>, | |
{ | |
GuardedTriple { | |
_phantom: PhantomData, | |
triple, | |
} | |
} | |
/// To be used in iterators, when Self::Triple is [*const T; 3] | |
fn guard_term_refs<'s, T>(&'s self, s: &'s T, p: &'s T, o: &'s T) -> GuardedTriple<'s, Self::Triple> where | |
T: AsRef<str> + ?Sized, | |
Self: Graph<Triple = [*const T; 3]>, | |
{ | |
GuardedTriple { | |
_phantom: PhantomData, | |
triple: [s, p, o], | |
} | |
} | |
} | |
// Testing the different ways to implement Graph iterators | |
struct Test1<T: Triple> (Vec<T>); | |
impl<T: Triple + Clone> Graph for Test1<T> { | |
type Triple = T; | |
fn triples<'s>(&'s self) -> GIterator<'s, Self> { | |
Box::new(self.0.iter().map(move |t| self.guard_triple(t.clone()))) | |
} | |
} | |
struct Test2<T: Triple> (Vec<T>); | |
impl<T: Triple + Clone> Graph for Test2<T> { | |
type Triple = *const T; | |
fn triples<'s>(&'s self) -> GIterator<'s, Self> { | |
Box::new(self.0.iter().map(move |t| self.guard_triple_ref(t))) | |
} | |
} | |
struct Test3<T: Triple> (Vec<T>); | |
impl<T> Graph for Test3<T> where | |
T: Triple + Clone, | |
{ | |
type Triple = [*const T::Term;3]; | |
fn triples<'s>(&'s self) -> GIterator<'s, Self> { | |
Box::new(self.0.iter().map(move |t| self.guard_term_refs(t.s(), t.p(), t.o()))) | |
} | |
} | |
fn main() { | |
let v = vec!["foo".to_string(), "bar".to_string()]; | |
println!("\nTest1"); | |
for t in Test1(v.clone()).triples() { | |
dbg!(&t); | |
println!("v: {}", t.s()); | |
} | |
println!("\nTest2"); | |
for t in Test2(v.clone()).triples() { | |
dbg!(&t); | |
println!("v: {}", t.s()); | |
} | |
println!("\nTest3"); | |
for t in Test3(v.clone()).triples() { | |
dbg!(&t); | |
println!("v: {}", t.s()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment