How to default-initialize a struct containing an array in Rust?

23.3k views Asked by At

What is the recommended way to declare a struct that contains an array, and then create a zero-initialized instance?

Here is the struct:

#[derive(Default)]
struct Histogram {
    sum: u32,
    bins: [u32; 256],
}

and the compiler error:

error[E0277]: the trait bound `[u32; 256]: std::default::Default` is not satisfied
 --> src/lib.rs:4:5
  |
4 |     bins: [u32; 256],
  |     ^^^^^^^^^^^^^^^^ the trait `std::default::Default` is not implemented for `[u32; 256]`
  |
  = help: the following implementations were found:
            <[T; 14] as std::default::Default>
            <&'a [T] as std::default::Default>
            <[T; 22] as std::default::Default>
            <[T; 7] as std::default::Default>
          and 31 others
  = note: required by `std::default::Default::default`

If I attempt to add the missing initializer for the array:

impl Default for [u32; 256] {
    fn default() -> [u32; 255] {
        [0; 256]
    }
}

I get:

error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
 --> src/lib.rs:7:5
  |
7 |     impl Default for [u32; 256] {
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl doesn't use types inside crate
  |
  = note: the impl does not reference any types defined in this crate
  = note: define and implement a trait or new type instead

Am I doing something wrong?

5

There are 5 answers

3
Matthieu M. On BEST ANSWER

Rust does not implement Default for all arrays because it does not have non-type polymorphism. As such, Default is only implemented for a handful of sizes.

You can, however, implement a default for your type:

impl Default for Histogram {
    fn default() -> Histogram {
        Histogram {
            sum: 0,
            bins: [0; 256],
        }
    }
}

Note: I would contend that implementing Default for u32 is fishy to start with; why 0 and not 1? or 42? There is no good answer, so no obvious default really.

2
Vladimir Matveev On

I'm afraid you can't do this, you will need to implement Default for your structure yourself:

struct Histogram {
    sum: u32,
    bins: [u32; 256],
}

impl Default for Histogram {
    #[inline]
    fn default() -> Histogram {
        Histogram {
            sum: 0,
            bins: [0; 256],
        }
    }
}

Numeric types have nothing to do with this case, it's more like problems with fixed-size arrays. They still need generic numerical literals to support this kind of things natively.

0
Andrew Wagner On

Indeed, at the time of writing, support for fixed-length arrays is still being hashed out in the standard library:

https://github.com/rust-lang/rust/issues/7622

0
guersam On

If you're sure to initialize every field with zero, this would work:

impl Default for Histogram {
    fn default() -> Histogram {
        unsafe { std::mem::zeroed() }
    }
}
0
Chayim Friedman On

If the array type is not Copy, the answer by @MatthieuM. won't work. In this case, instead of specifying each element explicitly (which is tedious and even impossible for generic array), you can use std::array::from_fn():

impl Default for Histogram {
    fn default() -> Histogram {
        Histogram {
            sum: Default::default(),
            bins: std::array::from_fn(|_| Default::default()),
        }
    }
}

You can also do it using array::map(), if you prefer:

impl Default for Histogram {
    fn default() -> Histogram {
        Histogram {
            sum: Default::default(),
            bins: [(); 256].map(|()| Default::default()),
        }
    }
}