Hi! I’m implementing a visitor pattern, with a lot of functions that look like this:
trait VisitMut {
#[must_use]
fn visit_decoration_element_mut(
&mut self, c: c::DecorationElement
) -> impl Iterator<Item = c::DecorationElement> {
use c::DecorationElement as D;
let r: Box<dyn Iterator<Item = D>> = match c {
D::Header(e) => Box::new(self.visit_header_mut(*e)),
D::Footer(e) => Box::new(self.visit_footer_mut(*e)),
};
r
}
}
Since it’s a trait, I can’t just have the function return Box<dyn Iterator<Item = c::DecorationElement>>
.
But I feel like ti’s ugly to assign the expression to a variable that is immediately returned in the next line, just so Rust knows the type.
Is there a better way to write this?
It's more general than -> impl Trait
; the compiler generally won't fall back on dyn
coercion to unify the types match
branches, array element expressions, etc, without some sort of cast or annotation.
Casting in the first match
branch is one alternative. Or you could factor it into a different function.
fn helper<V: VisitMut + ?Sized>(visit: &mut V, c: c::DecorationElement)
->
Box<dyn Iterator<Item = D>>
{
use c::DecorationElement as D;
match c {
D::Header(e) => Box::new(v.visit_header_mut(*e)),
D::Footer(e) => Box::new(v.visit_footer_mut(*e)),
}
}
trait VisitMut {
#[must_use]
fn visit_decoration_element_mut(
&mut self, c: c::DecorationElement
) -> impl Iterator<Item = c::DecorationElement> {
helper(self, c)
}
}
kpreid
April 3, 2025, 7:53pm
3
You could use a helper function that creates the specific Box you want, instead of for the whole match
:
fn box_iter(
i: impl Iterator<Item = c::DecorationElement>,
) -> Box<dyn Iterator<Item = c::DecorationElement>> {
Box::new(i)
}
...
match c {
D::Header(e) => box_iter(self.visit_header_mut(*e)),
D::Footer(e) => box_iter(self.visit_footer_mut(*e)),
}
...
hm, this could work, if I can make it generic over the Item
type, thanks!
/edit: I can, the function only needs lifetime annotation:
#[inline]
fn box_iter<'a, I>(i: impl Iterator<Item = I> + 'a) -> Box<dyn Iterator<Item = I> + 'a> {
Box::new(i)
}