0

I am trying to wrap an external type using my own type, as outlined in the linked rust playground.

The external type stores a closure on initialization:

struct ExternalType<P> {
    predicate: P,
}

impl<P> ExternalType<P>
where
    P: FnMut(u8) -> bool,
{
    fn new(predicate: P) -> Self {
        ExternalType { predicate }
    }
}

My "newtype" uses this ExternalType (without exposing it), but uses a different closure, e.g., a different argument type, which is converted internally:

struct MyType<P> {
    ext: ExternalType<P>,
}

impl<P> MyType<P>
where
    P: FnMut(u8) -> bool,
{
    fn new<Q>(user_predicate: Q) -> Self
    where
        Q: FnMut(u32) -> bool,
    {
        // This fails to compile since the closure is not of type `P`.
        let f: P = move |num| user_predicate(num as u32);
        Self {
            ext: ExternalType::new(f),
        }
    }
}

Without changing the signature of my user_predicate (the actual code is a bit more intricate), is there any way of storing this closure?

I know that every closure has its own type and therefore the implemented closure does not match the given type Q, but on the other hand I have no idea on how to implement or annotate this fact in my type and especially not in the impl block.

I've seen the question regarding on how to store a closure in a struct but it doesn't really answer my question.

martin
  • 591
  • 1
  • 8
  • 17
  • 1
    Use the `struct MyType(Box bool>);` adapted from the 2nd option on the answer on [How to store a closure inside Rust struct?](https://stackoverflow.com/questions/68066875/how-to-store-a-closure-inside-rust-struct) – cafce25 Apr 09 '23 at 18:57
  • No, as mentioned in the last sentence of my question I've already checked this. The problem I seem to have is that I need to annotate my `impl` block with a type that is different (?) than the closure I provide in the code. EDIT: Is there no way to use generics for that? I'd try not to use trait objects since this is also not always possible. – martin Apr 09 '23 at 18:58
  • There is no way to convert arbitrary type `Q` to arbitrary other type `P` even if they implement the same trait. – cafce25 Apr 09 '23 at 19:00
  • `Box<..>` doesn't really help me here since then rust complains that the closure needs to be `'static` to be stored. So there is also no other way to annotate this implementation? – martin Apr 09 '23 at 19:11
  • 2
    Ah yes, non `'static` lifetimes have to be explicitly named in struct definitions, `struct MyType<'a>(Box bool + 'a>);` and according changes across the example. – cafce25 Apr 09 '23 at 19:27
  • Yeah I've tried that, but then that failed in my "real word code" since I cannot really give a lifetime to the user predicate, which I'd expect to be moved into the predicate and then into the `Box` (if that is the correct way to explain what I'd hope would happen). – martin Apr 09 '23 at 19:36

1 Answers1

1

You can do this by instead of returning Self, return an impl type (playground)

fn new<Q>(mut user_predicate: Q) -> MyType<impl FnMut(u8) -> bool>
where
    Q: FnMut(u32) -> bool,
{
    let f = move |num| user_predicate(num as u32);
    MyType {
        ext: ExternalType::new(f),
    }
}

Future rust

This will be more ergonomic later with type alias impl trait. This will let you give the closure a name:

type P<Q> = impl FnMut(u8) -> bool;

which you can then use as MyType's generic:

impl<Q> MyType<P<Q>>

(playground)

drewtato
  • 6,783
  • 1
  • 12
  • 17
  • Using `impl` solves this problem. Maybe I can update the playground with a more intricate example where I use the type recursively, but then the problem is effectively that I am trying to use generics for two different closure types, so in the end the rust compiler seems to be correct to forbid this. It is just not "immediately visible" that this is a problem. – martin Apr 10 '23 at 07:20