Associated type on sized trait

I'm back again with yet another ridiculous question! I hit a puzzle while writing a macro. Say you have pairs of struct and trait. Is there any way to annotate them so you can name the struct at compile time given the name of the trait? Like this:

trait FooTrait {}
struct FooStruct;

trait FindLinkedStruct {
    type Output;
}

impl FindLinkedStruct for dyn FooTrait {
    type Output = FooStruct;
}

const VALUE: FooStruct = FooStruct;

// If we're allowed to type "FooTrait" and "VALUE", but not allowed to type
// "FooStruct", can we declare a var of type "FooStruct" anyway?
static OBJECTIVE: <dyn FooTrait as FindLinkedStruct>::Output = VALUE;

Yes it is possible! Except in my case, the trait is sized:

trait FooTrait: Sized {}

And with that my little scheme stopped working. Is there any way to fix it?

Why are you implementing this on dyn

In general dyn Trait is a pain to work with because it places a bunch of restrictions in order for types to be object-safe.

1 Like

I don't get the point, but, you could add a less restrictive supertrait.

Because it's the closest I've come to doing what I need. I already know my code is bad, that's why I posted here asking for help! :slight_smile:

And object-safety isn't the issue here. It's fine if the trait isn't object-safe. The issue is finding a way to use Rust syntax to navigate the compiler's symbol table.

The context is a set of macros I'm trying to slim down. I have some calls where I need to pass a lot of names around like macro!(Foo, FooParent, FooRef, FooMut, etc), and it's a bit longwinded and I'd like to replace it with just macro!(Foo).

Your link does compile, but it hits the same problem since at that point I only have access to FooTrait, so I can't drop in another ident like BarTrait, and I'm back where I started.

I wanted to avoid macros in the question because I thought they'd be distracting, but my other code was distracting in a different way.

Maybe this is a clearer problem statement.

trait FooTrait: Sized {}
struct FooStruct;

trait BarTrait: Sized {}
struct BarStruct;

macro_rules! define_constant {
    ($name:ident, $trait:path) => {
        static $name: Option<???> = None;
    }
}

define_constant!(CONSTANT, FooTrait);
define_constant!(CONSTANT, BarTrait);
// Desired output:
// static CONSTANT: Option<FooStruct> = None;
// static CONSTANT: Option<BarStruct> = None;

Can you define a macro that produces the desired output? You can define additional helpers outside the macro. The first post shows that it's possible without the Sized bound. If you add the Sized bound, is it still possible?

If your goal is to provide a bundle of definitions to macros, then one approach would be to use one or both of modules (to make a bundle of names that can be referred to starting from just one) and paste's ability to transform and construct identifiers (so that the name of the type/trait can be more as expected). Either one will suffice, but the former is simpler for internal stuff, the latter is nice for public names, and both together might or might not help with clean namespacing.

use paste::paste;

mod foo {
    trait FooTrait: Sized {}
    struct FooStruct;
}

mod bar {
    trait BarTrait: Sized {}
    struct BarStruct;
}

macro_rules! define_constant {
    ($name:ident, $ty_mod_name:path) => {
        paste::paste! {
            // can refer to the trait as $ty_mod_name:: [< $ty_mod_name:camel Trait >]
            static $name: Option< $ty_mod_name:: [< $ty_mod_name:camel Struct >] > = None;
        }
    }
}

define_constant!(CONSTANT, foo);
define_constant!(CONSTANT, bar);

Thanks for the idea. Paste seems like a very blunt hammer but it's working perfect!

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.