Hi all,
I'm writing a pipeline trait like this:
pub trait Transform<I> {
type Output<'o>;
fn transform(&self, input: I, output: impl for<'o> FnMut(Self::Output<'o>));
}
Which works out pretty nicely to build a chain of transformers. Full code in playground
Summary
/// Transformer that yields the given items
pub struct Yield<T> {
pub items: Vec<T>,
}
impl<T: Clone> Transform<()> for Yield<T> {
type Output<'o> = T;
fn transform(&self, _input: (), output: impl for<'o> FnMut(Self::Output<'o>)) {
self.items.iter().cloned().for_each(output)
}
}
/// Transformer that splits the input by whitespace
pub struct Words {}
impl<I: AsRef<str>> Transform<I> for Words {
type Output<'o> = &'o str;
fn transform(&self, input: I, output: impl for<'o> FnMut(Self::Output<'o>)) {
input.as_ref().split_ascii_whitespace().for_each(output)
}
}
/// Transformer that repeats every input a number of times
pub struct Repeat {
pub times: usize,
}
impl<I: Clone> Transform<I> for Repeat {
type Output<'o> = I;
fn transform(&self, input: I, mut output: impl for<'o> FnMut(Self::Output<'o>)) {
for _ in 0..self.times {
(output)(input.clone())
}
}
}
/// Transformer that prints the input to stdout
pub struct Print {}
impl<I: core::fmt::Display> Transform<I> for Print {
type Output<'o> = ();
fn transform(&self, input: I, mut output: impl for<'o> FnMut(Self::Output<'o>)) {
println!("{}", input);
(output)(()); // just signal
}
}
Because I can now run a pipeline like this
let start = Yield {
items: vec![String::from("hello world"), String::from("cookie dough")],
}; // just yield these inputs
let words = Words {}; // split strings by whitespace
let print = Print {}; // print items
let mut count = 0;
let mut terminating = |_| count += 1;
// closure needs type annotation in order to compile,
// https://users.rust-lang.org/t/implementation-of-fnonce-is-not-general-enough/68294/3
let mut chain = |w: &str| print.transform(w, &mut terminating);
let mut chain = |s| words.transform(s, &mut chain);
start.transform((), |s| chain(s));
assert_eq!(count, 4);
To write these chains I introduced:
/// Chain of two Transformers
pub struct Chain<T, U, I> {
t1: T,
t2: U,
marker_i: PhantomData<fn() -> I>,
}
impl<I, M, T: for<'i> Transform<I, Output<'i> = M>, U: Transform<M>> Chain<T, U, I> {
pub fn new(t1: T, t2: U) -> Self {
Self {
t1,
t2,
marker_i: PhantomData,
}
}
}
impl<I, M, T: for<'i> Transform<I, Output<'i> = M>, U: Transform<M>> Transform<I>
for Chain<T, U, I>
{
type Output<'o> = U::Output<'o>;
fn transform(&self, input: I, mut output: impl for<'o> FnMut(Self::Output<'o>)) {
let f = |u: T::Output<'_>| self.t2.transform(u, &mut output);
self.t1.transform(input, f);
}
}
Which works great for owned intermediate values
let start = Yield {
items: vec![String::from("hello world"), String::from("cookie dough")],
}; // just yield these inputs
let repeat = Repeat { times: 2 }; // repeat every input twice
let print = Print {}; // print items
let chain = Chain::new(start, repeat);
let chain = Chain::new(chain, print);
let mut count = 0;
chain.transform((), |_| count += 1);
assert_eq!(count, 4);
But fails to compile for borrowed intermediate values
let start = Yield {
items: vec![String::from("hello world"), String::from("cookie dough")],
}; // just yield these inputs
let words = Words {}; // split each input by whitespace
let print = Print {}; // print items
let chain = Chain::new(words, print);
let chain = Chain::new(start, chain);
let mut count = 0;
chain.transform((), |_| count += 1);
assert_eq!(count, 4);
with
error[E0599]: the method
transformexists for struct
Chain<Yield, Chain<Words, Print, String>, ()>, but its trait bounds were not satisfied
I could really use some help here. Are the Chain
trait bounds correct?