1

This is my very first Rust program that actually has a purpose and I'm still lost with the syntax. So I have trait objects like these:

trait HC<'a> {
    fn info(&self) -> TestInfo<'static>;
    fn test(&self) -> HCResult<'a>;
}

#[allow(non_camel_case_types)]
struct Test_NTP;
impl<'a> HC<'a> for Test_NTP {
    fn info(&self) -> TestInfo<'static> {
        TestInfo { ... }
    }

    fn test(&self) -> HCResult<'a> {
        ...
    }
}

#[allow(non_camel_case_types)]
struct Test_Last;
impl<'a> HC<'a> for Test_Last {
    fn info(&self) -> TestInfo<'static> {
        TestInfo { ... }
    }

    fn test(&self) -> HCResult<'a> {
        ...
    }
}

I want to construct an array of these objects those define the HC trait, tupled with an ID, so I can iterate through them and call their test() functions.

const HC_TESTS: [(i16, &dyn HC)] = [
    (1, &Test_NTP),
    (2, &Test_Last)
];

I tried several variations, but I always get a "doesn't have a size known at compile-time" error. I thought putting the objects in Boxes would solve the problem, but I get the same error:

error[E0277]: the size for values of type `[(i16, Box<&'static (dyn HC<'static> + 'static)>)]` cannot be known at compilation time
   --> src/hc.rs:125:17
    |
125 | const HC_TESTS: [(i16, Box<&dyn HC>)] = [
    |                 ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
    |
    = help: the trait `Sized` is not implemented for `[(i16, Box<&'static (dyn HC<'static> + 'static)>)]`

The structs don't have any fields, the only reason I use them is to bound the info() and test() functions together, and I don't even use &self, they're only needed to dispatch virtual methods. What am I doing wrong?

MegaBrutal
  • 310
  • 2
  • 9
  • [Works for me](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=71f8cd8bebff8ce9aedf41eaa604786f) so the problem must be somewhere else, but we will need a [mre] to diagnose further. – Jmb May 08 '22 at 06:15

2 Answers2

2

Your syntax is off just slightly. The error

the trait `Sized` is not implemented for `[(i16, Box<&'static (dyn HC<'static> + 'static)>)]

refers to the most outer [...], which is a slice.

In

const HC_TESTS: [(i16, Box<&dyn HC>)] =

you explicitly set the type of HC_TESTS to be a slice, not an array. A slice is always unsized, so the compiler complains. What you want is simply

const HC_TESTS: [(i16, Box<&dyn HC>); 2] =

which sets the type of HC_TESTS to be an array of exactly two elements.

Notice that the other answers and comments here ("works for me") use this syntax as well.

user2722968
  • 13,636
  • 2
  • 46
  • 67
  • Thank you, this was the problem. I thought Rust would know the size of the array by the amount of items listed. Reminds me of my Pascal days! But of course it needs something to differentiate between array and slice. – MegaBrutal May 09 '22 at 21:00
1

Your problem is that you cannot use & (you actually can, the problem is that you didn't actually set the array length [(i16, &dyn HC); 2]).

Also you could have use Box. But since Box::new is not const, you would need to wrap it in some other lazy wrapper, std::lazy::Lazy is not stable jet but would work:

const HC_TESTS: Lazy<[(i16, Box<dyn HC>); 2]> = Lazy::new(|| [
    (1, Box::new(Test_NTP)),
    (2, Box::new(Test_Last))
]);

Playground

Meanwhile you can use once_cell Lazy, which works the same.

Another option is to wrap all of them in a enum, so you can construct them, and impl the trait for that enum so you can use it later:

enum Tests {
    NTP(Test_NTP),
    Last(Test_Last)
}

impl<'a> HC<'a> for Tests {
    fn info(&self) -> TestInfo<'static> {
        match self {
            Self::NTP(e) => e.info(),
            Self::Last(e) => e.info()
        }
    }

    fn test(&self) -> HCResult<'a> {
        match self {
            Self::NTP(e) => e.test(),
            Self::Last(e) => e.test()
        }
    }
}


const HC_TESTS: [(i16, Tests); 2] = [
    (1, Tests::NTP(Test_NTP)),
    (2, Tests::Last(Test_Last))
];

Playground

Netwave
  • 40,134
  • 6
  • 50
  • 93
  • 1
    You can create the entire array inside `Lazy`. – Chayim Friedman May 08 '22 at 04:10
  • Why do you say he can't use `&`? [Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=71f8cd8bebff8ce9aedf41eaa604786f) – Jmb May 08 '22 at 06:15
  • @Jmb, oh, how does that work?. Does it allocate an static object? – Netwave May 08 '22 at 13:26
  • @Netwave yes, so long as the object _can_ be static allocated (e.g. no `Box`, `Vec`, maps or similar). – Jmb May 08 '22 at 15:18
  • @Jmb, well that makes sense. I never had the need so didn't knew about it. Thanks for the tip!! – Netwave May 08 '22 at 16:50
  • Thanks for the tip of wrapping them in Enum, though I think it would just add unnecessary complexity in this case. Now I just go through them with for: "for (code, test) in HC_TESTS ... test.test()", code will be used to form short identifiers those can be easily searched in documentation (e.g. "HC001: NTP test failed" -> user looks up in doc or calls info() through "--info HC001" to see how to resolve it). But this Enum dispatch trick might be useful in the future regardless. – MegaBrutal May 09 '22 at 21:15