Error: expected function, tuple struct or tuple variant, found type alias `RGBSpectrum`

Hello All,

Context

So, I am working on a rendering tool and I wanted to let the users to freely choose the colourspace they want. Apart from the Blah-Blah, this means that I want to implement something like the following:

// This is the core struct. 
struct Spectrum<const N: usize>(pub [f32; N]);
// This is the alias
type RGBSpectrum = Spectrum<3>;

fn main() {
    // I'd like to initialise an alias as follows
    let rgb = RGBSpectrum([1., 2., 3.]);
}

But, I get this error (LINK to playground):

   Compiling playground v0.0.1 (/playground)
error[E0423]: expected function, tuple struct or tuple variant, found type alias `RGBSpectrum`
 --> src/main.rs:9:15
  |
3 | struct Spectrum<const N: usize>(pub [f32; N]);
  | ---------------------------------------------- similarly named tuple struct `Spectrum` defined here
...
9 |     let rgb = RGBSpectrum([1., 2., 3.]);
  |               ^^^^^^^^^^^ help: a tuple struct with a similar name exists: `Spectrum`
  |
  = note: can't use a type alias as a constructor

For more information about this error, try `rustc --explain E0423`.
error: could not compile `playground` due to previous error

Question

Why isn't this allowed? Is this a bug in the compiler or parser or whatever (I would guess it is not)?

When you declare a tuple struct, you actually declare two different things with the same name [1]:

  • A type
  • A function item (the constructor)

The type alias only aliases the type. You could do this:

type RGBSpectrum = Spectrum<3>;

#[allow(non_snake_case)]
fn RGBSpectrum(data: [f32; 3]) -> RGBSpectrum {
    Spectrum(data)
}

Playground.


  1. Rust has a handful of namespaces, so a type and a function can have the same name ↩︎

1 Like

Brilliant.

Would it make sense to add this out of the box as opposed to having to write that function which does not comply with the camel case convention?

You mean, should a type alias do it automatically? Maybe it should have. But for that not to be a breaking change at this point, the compiler would have to not generate one for you if something else with the same name in the value namespace exists, or it would have to be opt-in.

Thinking a little longer though, I don't think it should have [1]. You can make aliases such as:

type Foo<T> = <T as Iterator>::Item;

But you can't write something like

fn Foo<T, P1, P2, ..., PK>(P1, P2, ..., PK) ->  Foo<T>
where
    T: Iterator<Item = (P1, P2, ..., PK)>
    OR
    T: Iterator<Item = Spectrum<K>>
    OR
    ...
{
   // ...
}

So it could only work sometimes anyway.


  1. or that making something that does so would be that simple ↩︎

1 Like

One funny thing is that you can use the field syntax to construct with the type alias:

    let rgb = RGBSpectrum { 0: [1., 2., 3.] };
3 Likes

Another suggestion is that associated methods do work across the type alias too:

impl<const N: usize> Spectrum<N> {
    fn new(data: [f32; N]) -> Self {
        Self(data)
    }
}

So RGBSpectrum::new(...) will work fine.

2 Likes

this is actually a good solution. I'll think about all the options here

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.