0

I am trying to create a struct that contains an array of items that each have an associated name and value. I am using a struct here instead of just an array because I may want to add additional fields to the struct once I get this part working.

I came up with the following:

pub trait Named {
    fn name(&self) -> &str;
}

pub trait Valued<T> {
    fn value(&self) -> &T;
}

pub struct ValuedEnum<T, D, const N: usize>
where
    T: Named + Valued<D>,
{
    options: [T; N],
}

which when compiled generates this error message:

error[E0392]: parameter `D` is never used
  --> src\inputs\definition\macros.rs:68:26
   |
68 | pub struct ValuedEnum<T, D, const N: usize>
   |                          ^ unused parameter
   |
   = help: consider removing `D`, referring to it in a field, or using a marker such as `PhantomData`
   = help: if you intended `D` to be a const parameter, use `const D: usize` instead

So I tried removing D:

pub trait Named {
    fn name(&self) -> &str;
}

pub trait Valued<T> {
    fn value(&self) -> &T;
}

pub struct ValuedEnum<T, const N: usize>
where
    T: Named + Valued<D>,
{
    options: [T; N],
}

But then I got this error:

error[E0412]: cannot find type `D` in this scope
  --> src\inputs\definition\macros.rs:70:23
   |
68 | pub struct ValuedEnum<T, const N: usize>
   |                       - similarly named type parameter `T` defined here
69 | where
70 |     T: Named + Valued<D>,
   |                       ^
   |
help: a type parameter with a similar name exists
   |
70 |     T: Named + Valued<T>,
   |                       ~
help: you might be missing a type parameter
   |
68 | pub struct ValuedEnum<T, const N: usize, D>
   |                                        +++

Which seems to be taking me in a loop.

Is there a correct way to specify this?

Ben Booth
  • 61
  • 6

1 Answers1

0

It seems like the correct answer may be this:

pub trait Named {
    fn name(&self) -> &str;
}

pub trait Valued<T> {
    fn value(&self) -> &T;
}

pub struct ValuedEnum<T,D, const N: usize>
where
    T: Named + Valued<D>,
{
    options: [T; N],
    phantom: PhantomData<D>,
}

Where the PhantomData is a zero-sized marker to tell the compiler that I really do need type D

Or alternatively using associated types:

pub trait Named {
    fn name(&self) -> &str;
}

pub trait Valued {
    type Return;
    fn value(&self) -> &Self::Return;
}

pub struct ValuedEnum<T, const N: usize>
where
    T: Named + Valued,
{
    options: [T; N],
}
Ben Booth
  • 61
  • 6
  • Actually you probably shouldn't put bounds on the `struct` declaration: `struct ValuedEnum {…}`. See: https://stackoverflow.com/questions/49229332/should-trait-bounds-be-duplicated-in-struct-and-impl – Jmb Dec 17 '21 at 13:59
  • Ok. So how do I implement the intended functionality without using a bound on the type? If I pass a ValuedEnum somewhere, isn't the bound required so that the consumer of the ValuedEnum knows that the name() and value() methods can be called on the items in the array? – Ben Booth Dec 17 '21 at 14:06
  • Have you read the link I posted? Unless you know what you're doing, you should put the bounds on `impl` blocks, _not_ on `struct` declarations. – Jmb Dec 17 '21 at 14:16