playground
I define a nested tree using trait Node
for which I have two implementations struct NodeSingle
and struct NodeSet
, which differ in their payload (a single usize vs. a set of usize).
The trait Node
requires that an iterator over the payload is returned but the type of this iterator depends on the struct implementing the Node
trait (std::iter::Once<usize>
vs. std::collections::hash_set::Iter<'a, usize>
). So this should lead to dynamic dispatch as far as I understand.
I am a bit lost now that the code does not compile after adding some Box<..>
around dyn ...
in some places as suggested by the compiler.
Also, maybe only the usage of the code is incorrect as only the compilation of the test code fails now.
I guess, to switch to static dispatch, I would need to wrap the returned iterator into an enum capturing precisely the two allowed implementations of the trait Node
(I saw something like this here / Simple Iterator Delegation; where also this example is referenced).
I have not tried this but I would not really be a fan of this either as I may want to allow users of my library to eventually extend the allowed implementations.
the code from the playground
#![allow(dead_code)]
use std::collections::{HashSet};
use std::fmt::{Debug};
use std::iter;
type BoxedNode<'a> = Box<dyn Node<'a, IterElements=Box<dyn Iterator<Item=usize> + 'a>>>;
trait Node<'a>: Debug {
type IterElements;
fn elements(&'a self) -> Self::IterElements;
fn nested(&'a self) -> Option<&'a BoxedNode<'a>>;
}
#[derive(Debug)]
struct NodeSingle<'a> {
data: usize,
nested: Option<BoxedNode<'a>>,
}
impl<'a> Node<'a> for NodeSingle<'a> {
type IterElements = Box<std::iter::Once<usize>>;
fn elements(&'a self) -> Self::IterElements {
Box::new(iter::once(self.data))
}
fn nested(&'a self) -> Option<&'a BoxedNode<'a>> {
match &self.nested {
None => { None }
Some(x) => { Some(&x) }
}
}
}
#[derive(Debug)]
struct NodeSet<'a> {
data: HashSet<usize>,
nested: Option<BoxedNode<'a>>,
}
impl<'a> Node<'a> for NodeSet<'a> {
type IterElements = Box<std::collections::hash_set::Iter<'a, usize>>;
fn elements(&'a self) -> Self::IterElements {
Box::new(self.data.iter())
}
fn nested(&'a self) -> Option<&'a BoxedNode<'a>> {
match &self.nested {
None => { None }
Some(x) => { Some(&x) }
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test() {
let mut node_single = NodeSingle { data: 0, nested: None };
let mut set = HashSet::default();
set.insert(1);
set.insert(2);
let node_set = NodeSet { data: set, nested: None };
node_single.nested = Some(Box::new(node_set));
println!("{:?}", node_single);
}
}
compile error
Compiling playground v0.0.1 (/playground)
error[E0271]: type mismatch resolving `<NodeSet<'_> as Node<'_>>::IterElements == Box<dyn Iterator<Item = usize>>`
--> src/lib.rs:67:35
|
67 | node_single.nested = Some(Box::new(node_set));
| ^^^^^^^^^^^^^^^^^^ type mismatch resolving `<NodeSet<'_> as Node<'_>>::IterElements == Box<dyn Iterator<Item = usize>>`
|
note: expected this to be `Box<dyn Iterator<Item = usize>>`
--> src/lib.rs:42:25
|
42 | type IterElements = Box<std::collections::hash_set::Iter<'a, usize>>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: expected struct `Box<dyn Iterator<Item = usize>>`
found struct `Box<std::collections::hash_set::Iter<'_, usize>>`
= help: `std::collections::hash_set::Iter<'_, usize>` implements `Iterator` so you could box the found value and coerce it to the trait object `Box<dyn Iterator>`, you will have to change the expected type as well
= note: required for the cast from `Box<NodeSet<'_>>` to `Box<dyn Node<'_, IterElements = Box<dyn Iterator<Item = usize>>>>`
For more information about this error, try `rustc --explain E0271`.
error: could not compile `playground` (lib test) due to 1 previous error
The error message is confusing to me as (in help:
) it states that the concrete iterator (seems to) implement the Iterator trait as required. Also the following suggestion seems to suggest to use another Box wrapping...