Hi,
I have a number of functions. Each function takes an iterator and transforms it to another iterator.
For example:
Fn1: Iter -> Iter
Fn2: Iter -> Iter
Fn3: Iter -> Iter
I then want to create a pipeline object which composes ("chains" in some sense) each function into one. So for example:
Pipeline: Iter -> Iter = Fn3(Fn2(Fn1))
i.e. the pipeline function is the same as applying the first function Fn1, then the second Fn2 and then third Fn3.
I think I have achieved this with a minimal example (below and playground). However the compiler complains about lifetimes of the various objects which I can't resolve. Can you please help?
#[allow(dead_code)]
#[allow(unused_variables)]
// ----- Iterator to Iterator Transformers
trait IterTransformer<'a> {
type InItem;
type OutItem;
fn transform(
&'a self,
items: Box<dyn Iterator<Item = Self::InItem> + 'a>,
) -> Box<dyn Iterator<Item = Self::OutItem> + 'a>;
}
struct IT1 {}
impl<'a> IterTransformer<'a> for IT1 {
type InItem = &'a String;
type OutItem = String;
fn transform(
&'a self,
items: Box<dyn Iterator<Item = Self::InItem> + 'a>,
) -> Box<dyn Iterator<Item = Self::OutItem> + 'a> {
Box::new(items.flat_map(move |s| s.split(" ")).map(|s| s.to_string()))
}
}
struct IT2 {}
impl<'a> IterTransformer<'a> for IT2 {
type InItem = String;
type OutItem = Vec<u8>;
fn transform(
&'a self,
items: Box<dyn Iterator<Item = Self::InItem> + 'a>,
) -> Box<dyn Iterator<Item = Self::OutItem> + 'a> {
Box::new(items.map(|x| Vec::from(x.as_bytes())))
}
}
// ----- Pipeline composition
struct Pipeline<T, V> {
transform_closure: Box<dyn Fn(Box<dyn Iterator<Item = T>>) -> Box<dyn Iterator<Item = V>>>,
}
impl<T, V> Pipeline<T, V> {
fn transform(&self, items: Box<dyn Iterator<Item = T>>) -> Box<dyn Iterator<Item = V>> {
(self.transform_closure)(items)
}
fn add_step<'a, U>(
&mut self,
step: impl IterTransformer<'a, InItem = V, OutItem = U>,
) -> Box<Pipeline<T, U>> {
Box::new(Pipeline {
transform_closure: Box::new(|x| step.transform((self.transform_closure)(x))),
})
}
}
fn main() {
let input = vec!["Mary had a little lamb.".to_string(), "One two three.".to_string()].iter();
let iter_transformer_1 = IT1 {};
let iter_transformer_2 = IT2 {};
let mut pipeline = Pipeline {
transform_closure: Box::new(|x| iter_transformer_1.transform(x)),
};
let pipeline = pipeline.add_step(iter_transformer_2);
let output = pipeline.transform(Box::new(input)).collect::<Vec<Vec<u8>>>();
println!("{:?}", output);
}
The current compiler error:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/main.rs:59:41
|
59 | transform_closure: Box::new(|x| step.transform((self.transform_closure)(x))),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 54:5...
--> src/main.rs:54:5
|
54 | / fn add_step<'a, U>(
55 | | &mut self,
56 | | step: impl IterTransformer<'a, InItem = V, OutItem = U>,
57 | | ) -> Box<Pipeline<T, U>> {
... |
60 | | })
61 | | }
| |_____^
note: ...so that the types are compatible
--> src/main.rs:59:41
|
59 | transform_closure: Box::new(|x| step.transform((self.transform_closure)(x))),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: expected `&&mut Pipeline<T, V>`
found `&&mut Pipeline<T, V>`
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that the expression is assignable
--> src/main.rs:59:32
|
59 | transform_closure: Box::new(|x| step.transform((self.transform_closure)(x))),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: expected `std::boxed::Box<(dyn std::ops::Fn(std::boxed::Box<(dyn std::iter::Iterator<Item = T> + 'static)>) -> std::boxed::Box<(dyn std::iter::Iterator<Item = U> + 'static)> + 'static)>`
found `std::boxed::Box<dyn std::ops::Fn(std::boxed::Box<(dyn std::iter::Iterator<Item = T> + 'static)>) -> std::boxed::Box<dyn std::iter::Iterator<Item = U>>>`
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/main.rs:59:45
|
59 | transform_closure: Box::new(|x| step.transform((self.transform_closure)(x))),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the method body at 54:17...
--> src/main.rs:54:17
|
54 | fn add_step<'a, U>(
| ^^
note: ...so that the declared lifetime parameter bounds are satisfied
--> src/main.rs:59:45
|
59 | transform_closure: Box::new(|x| step.transform((self.transform_closure)(x))),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that the expression is assignable
--> src/main.rs:59:32
|
59 | transform_closure: Box::new(|x| step.transform((self.transform_closure)(x))),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: expected `std::boxed::Box<(dyn std::ops::Fn(std::boxed::Box<(dyn std::iter::Iterator<Item = T> + 'static)>) -> std::boxed::Box<(dyn std::iter::Iterator<Item = U> + 'static)> + 'static)>`
found `std::boxed::Box<dyn std::ops::Fn(std::boxed::Box<(dyn std::iter::Iterator<Item = T> + 'static)>) -> std::boxed::Box<dyn std::iter::Iterator<Item = U>>>`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0495`.
error: could not compile `rust_sandbox`.
Any help would be much appreciated. As I said I'm not sure how to resolve the lifetime issues.