Is it possible to make this code compile without using Box dyn?

I was trying really hard to remove Box dyn so we don't have runtime cost for this. But I personally can't find a better way to do this. The best I can came out is below. Is it possible to do so without runtime cost?

The basic idea is, I want define a function that take a closure as argument. The closure takes a slice reference as argument, and return an iterator that refering the slice.

For example, the code compiled with Box dyn is below:

fn foo<F>(make_iter: F) where for<'a> F: Fn(&'a mut [i32])->Box<dyn Iterator<Item=&'a mut i32>+'a>{
    let mut data = vec![1, 2, 3, 4];
    // Note: I want data defined in the function

    make_iter(&mut data);
}

fn main() {
    foo(|x| Box::new(x.iter_mut()));
    foo(|x| Box::new(x.iter_mut().rev()));
}

I can't come out a nice way to write this using impl Iterator<...>, is it even possible to do so at the moment? If can't, how do we write this in a more idiomatical way?

Thanks a lot!

PS: This question is initially asked in stackoverflow rust - How to define a generic function that takes a function that converts a slice to an iterator - Stack Overflow

trait IterConstructor<'a>: FnOnce(&'a mut [i32]) -> Self::Iter {
    type Iter: Iterator<Item = &'a mut i32> + 'a;
}

impl<'a, F, I> IterConstructor<'a> for F
where
    F: FnOnce(&'a mut [i32]) -> I,
    I: 'a + Iterator<Item = &'a mut i32>
{
    type Iter = I;
}

fn foo<F>(make_iter: F) where for<'a> F: IterConstructor<'a> {
    let mut data = vec![1, 2, 3, 4];
    make_iter(&mut data);
}

This compiles, however neither foo(|data| data.iter_mut()); nor foo(|data: &mut [i32]| data.iter_mut()); will work because of rust-lang/rust#70263

5 Likes

Thanks a lot for coming out this solution :grinning:. Very insightfull. Althought the example doesn't compile now, but I image this will eventually be compiled in the future when the issue is fixed. Is there a rough estimation for this?

It's not a blocking issue for anything, so who knows when it will be fixed.

While not the prettiest solution, when the type-enforced restrictions from generics are too constraining due to a current lack of expressivity in the language, if you really want to get rid of the dyn-dispatch (the Box heap allocation can be removed with a call-site that happens to be a bit more cumbersome), then you always have the option of something lazily type checked such as C++ templates. In Rust, these are macros:

#[doc(hidden)] /** Not part of the public API */ pub
fn __ty_check__<'lt, F, R> (f: F)
  -> F
where
    F : FnOnce(&'lt mut [i32]) -> R,
    R : 'lt + Iterator<Item = &'lt mut i32>,
{
    f
}
    
macro_rules! foo {(
    $make_iter:expr $(,)?
) => (
    match $crate::__ty_check__($make_iter) { make_iter => {
        let mut data = ::std::vec![1, 2, 3, 4];
        let iter = make_iter(&mut data);
        …
    }}
)}
pub(crate) use foo;

fn main ()
{
    foo!(|x| x.iter_mut());
    foo!(|x| x.iter_mut().rev());
}
4 Likes

This is brilliant. Thanks for the help.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.