Basically I want to something like the following to work. In order to do this I need to store a list of closures with different possible return values. All I care about is that the return value is Display
.
fn main() {
let mut input = vec![0; 10];
let mut ex = Example::default();
ex.push(|| 5);
ex.push(|| "hello");
ex.push(|| { input.reverse(); input[0] });
}
First attempt
use std::fmt::Display;
#[derive(Default)]
pub struct Example {
funcs: Vec<Box<dyn FnMut() -> dyn Display>>,
}
impl Example {
pub fn push(&mut self, func: dyn FnMut() -> dyn Display) {
self.funcs.push(Box::new(func))
}
}
I quickly realized that this had multiple problems:
- Not being able to pass a closure easily to
.push()
. -
func
arg is notSized
Second attempt, generics
use std::fmt::Display;
#[derive(Default)]
pub struct Example {
funcs: Vec<Box<dyn FnMut() -> dyn Display>>,
}
impl Example {
pub fn push<F>(&mut self, func: F)
where
F: FnMut() -> (dyn Display) + 'static
{
self.funcs.push(Box::new(func))
}
}
This doesn't work:
the size for values of type `(dyn std::fmt::Display + 'static)` cannot be known at compilation time
I tried to add a Sized
constraint but couldn't seem to get it to work. Maybe there is a way?
Third attempt, more generics
use std::fmt::Display;
#[derive(Default)]
pub struct Example {
funcs: Vec<Box<dyn FnMut() -> dyn Display>>,
}
impl Example {
pub fn push<F, R>(&mut self, func: F)
where
R: Display,
F: FnMut() -> R
{
self.funcs.push(Box::new(func))
}
}
This doesn't work
expected trait object `dyn std::fmt::Display`, found type parameter `R`
Is it possible to cast a type parameter that implements the correct types to a trait object?
Wrapper type
use std::fmt;
struct Display<'a> {
inner: Box<dyn fmt::Display + 'a>,
}
impl fmt::Display for Display<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.as_ref().fmt(f)
}
}
#[derive(Default)]
pub struct Example<'a> {
funcs: Vec<Box<dyn FnMut() -> Display<'a> + 'a>>,
}
impl<'a> Example<'a> {
pub fn push<F, R>(&mut self, mut func: F)
where
R: fmt::Display + 'a,
F: FnMut() -> R + 'a,
{
self.funcs.push(Box::new(move || Display { inner: Box::new(func()) }))
}
}
This works, but it now seems quite complicated. Is there anyway to do this without the wrapper type? Or a better way entirely?