Creating a DST struct with a dyn FnMut

Hi, I'm looking to create instances of this struct:

struct DelayedConstraint<'s, 'acceptor> {
    next: Option<Box<Self>>,
    f: dyn FnMut() -> Result<(), &'acceptor dyn DelayedConstraintAcceptor<'s>> + 's,
}

I thought this'd be a nice exercise for DSTs. I needed a linked list for these anyway, and by including the closure object in the DelayedConstraint itself, I save a Box.

I'm seeing some crates that help with constructing DSTs, like dst-factory, or dyn_struct, but they seem to be awfully limited in what they support, and this support does not seem to include dyn function pointers.

What's right now the idiomatic way to construct/destruct such types? Likely there's no safe way, so I'm happy to reach for unsafe as well.

In this case, you can make use of the built-in facilities of the language. The key is that your struct must be generic over the function type, but the actual list need not be.

struct DelayedConstraint<'s, 'acceptor, F = dyn FnMut() -> Result<(), &'acceptor dyn DelayedConstraintAcceptor<'s>> + 's>
where
    F: ?Sized
{
    next: Option<Box<DelayedConstraint<'s, 'acceptor>>>,
    f: F,
}

Then you can create a Box<DelayedConstraint<'s, 'acceptor, _>> with a closure, and coerce it to Box<DelayedConstraint<'s, 'acceptor>>.

(You can also use a type alias instead of the default on the type parameter.)

2 Likes

Whoa,

So, I've got this code, and indeed the compiler doesn't complain:

struct DelayedConstraint<
    's,
    'acceptor,
    F = dyn FnMut() -> Result<(), &'acceptor dyn DelayedConstraintAcceptor<'s>> + 's,
> where
    F: ?Sized,
{
    next: Option<Box<DelayedConstraint<'s, 'acceptor>>>,
    f: F,
}

fn mk<'s, 'acceptor>(
    f: impl FnMut() -> Result<(), &'acceptor dyn DelayedConstraintAcceptor<'s>> + 's,
) -> Box<DelayedConstraint<'s, 'acceptor>> {
    Box::new(DelayedConstraint { next: None, f })
}

I'm wrapping my head around why it actually works, but now I think I see it.

The trick is, that there is a subtyping coersion MyStruct<impl Trait> -> MyStruct<dyn Trait>.
The type we like to hold is DelayedConstraint<dyn FnMut...>,
but to create it, we have to pass through types with concrete implementations. (DelayedConstraint<impl FnMut...>)

There's no special Box-related rules here, right?

This does not involve any subtyping — it is an unsized coercion, and neither type is a subtype of the other.

Not Box specifically, but you can only do this with pointer types which implement CoerceUnsized, which is not currently possible to do in stable Rust, so it is limited to the set Box, Rc, Arc, &, &mut, ...

2 Likes