Derive Default trait for type aliases

#[derive(Default)]
type a = [isize; 100];

Apparently, this is allowed for structures, enumerations and unions but not for type aliases. Why? Is there an alternative?

This is because type aliases are not types, they're simply another name for an existing type. struct, enum and union items all declare a new type.

The fact that type is just a type alias causes two technical limitations:

First, the derive no longer has any way to get the implementation / body of the type. Say I write this:

struct X {
    a: i32,
    b: i32,
    c: i32,
}
#[derive(Default)]
type a = X;

All derive(Default) will receive is the data "type a = X;". This doesn't tell it anything about the fields X has. Notably, I think what I'd expect this to generate is something like:

impl Default for X {
    fn default() -> Self {
        X { a: i32::default(), b: i32::default(), c: i32::default() }
    }
}

But since derive(Default) doesn't know about a, b and c, it can't.

Second, trait implementations must contain a "local" type. By that, I mean you either must be implementing a trait you define, or implementing it on a type you define. Since derives are really just macros expanding to trait implementations, this applies to them too.

Neither Default, nor [isize; 100] was defined in your crate. Both are defined by std - and thus you can't write

impl Default for [isize; 100] { ... }

(and so, neither can #[derive(Default)] in your code)


I think the most common general workaround here is to use a wrapper "newtype" struct. Instead of declaring a type alias, you could write

#[derive(Default)]
struct a([isize; 100]);

More on this here: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits-on-external-types


But given your particular example, I expect that won't work. [isize; 100] doesn't implement Default, and all #[derive(Default)] does is call default on a struct's fields. So this won't exactly work.

If you do need a default-able [isize; 100], I recommend using a wrapper struct like above, and just implementing Default manually. Maybe something like this?

impl Default for a {
    fn default() -> Self {
        a([0isize; 100])
    }
}

Or, if you need more traits than just Default and/or don't want to go to the trouble, there are crates which work around this limitation. In particular, https://docs.rs/arrayvec/0.5.1/arrayvec/ offers nice array-like things (still stack based, but with a runtime-determined length) and ArrayVec<[isize; 100]> implements many more traits than [isize; 100] does.

3 Likes

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.