Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Initialising empty structs in Rust.

Initialising Empty Structs in Rust

In C/C++, you can initialise a struct without giving values for any of the fields:

struct Point {
  float x;
  float y;
  float z;
};

int main() {
  Point my_point = {};
}

Structs in RUST can't do this by default, you'll just get an error:

#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
    z: i32,
}

fn main() {
    let p1 = Point{};
    println!("{:?}", eep);
}
error[E0063]: missing fields `x`, `y`, `z` in initializer of `Point`
 --> src\main.rs:2:15
  |
2 |     let p1 = Point{};
  |              ^^^^^ missing `x`, `y`, `z`

The proper way to do this for a struct in Rust is to implement the Default trait and then you can generate default values easily:

#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
    z: i32,
}

impl Default for Point {
    fn default () -> Point {
        Point{x: 0, y: 0, z:0}
    }
}
fn main() {
  let p1 = Point::default(); 
  let p2 = Point{ x: 34, ..Default::default() }; // Partial definition of fields
}

You can even do this automatically using the derive attribute.

#[derive(Debug, Default)] // Derive is cool, I have no idea how it works!
struct Point {
    x: i32,
    y: i32,
    z: i32,
}

fn main() {
  let p1 = Point::default();
  let p2 = Point{ x: 34, ..Default::default() };
}

It's like magic!

Initialising empty structs is especially useful when working with an API, where you might give a function a pointer to a struct and the fields are populated for you. If you're working with a RUST API that follows this pattern, we can just use our Default trait implementation to do this, right? Well, that depends on the API. If you're using using the winapi crate, this doesn't work as Default has not been implemented for any of the structs that I've used:

extern crate winapi;

use winapi::windef::RECT;

fn main() {
    let rect = RECT{ ..Default::default() };
    println!("{:?}", rect);
}
error[E0277]: the trait bound `winapi::RECT: std::default::Default`
is not satisfied
 --> src\main.rs:6:24
  |
6 |     let rect = RECT{ ..Default::default() };
  |                        ^^^^^^^^^^^^^^^^ trait `winapi::RECT: std::default::Default` not satisfied

Unfortunately, you're not allowed to implement a trait that you did not define, for a type that you also did not define. So if you're using a struct from an external crate, you can't implement Default for it:

extern crate winapi;

use winapi::windef::RECT;

impl Default for RECT {
    fn default () -> RECT {
        RECT{left: 0, top: 0, right: 0, bottom: 0}
    }
}

fn main() {
    let rect = RECT::default();
    println!("{:?}", rect);
}
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
 --> src\main.rs:5:1
  |
5 | impl Default for RECT {
  | ^ impl doesn't use types inside crate

That's annoying... So what do you do? There's a couple of things you could do. Firstly, you could wrap the struct as a new type (so you're defining it in your own crate):

extern crate winapi;

use winapi::windef::RECT;

#[derive(Debug)]
struct WrappedRECT{rect: RECT}

impl Default for WrappedRECT {
    fn default () -> WrappedRECT {
        WrappedRECT{rect: RECT{left: 0, top: 0, right: 0, bottom: 0}}
    }
}

fn main() {
    let rect = WrappedRECT::default();
    println!("{:?}", rect);
}

But this is a bit clunky, so I prefer just creating a new trait, and implementing it for the external struct:

extern crate winapi;

use winapi::windef::RECT;

trait Empty<T> {
    fn empty() -> T;
}

impl Empty<RECT> for RECT {
    fn empty() -> RECT {
        RECT{left: 0, top: 0, right: 0, bottom: 0}
    }
}

fn main() {
    let rect = RECT::empty();
    println!("{:?}", rect);
}

It seems a little more transparent, and there's no clash with the name of the method. If you want to be a good citizen, the best way to deal with this is probably to just go and modify the crate you're using, adding derive(Debug) attributes to everything!

Thanks to joshtriplett and yohanesu75 for some extra info.

@joshtriplett

This comment has been minimized.

Copy link

joshtriplett commented Dec 10, 2016

When you derive Default for a struct, you don't need to initialize it like Point { ..Default::default() }; you can write Point::default(). See https://is.gd/HbIA8k for an example.

@johalun

This comment has been minimized.

Copy link

johalun commented Dec 10, 2016

Thanks for the write up. This is great for beginners :)
By the way, if the compiler can infer the type you can even do
let p = Default::default();
or maybe you need to give a hint
let p: Point = Default::default();
How cool is that :)

@ChrisWellsWood

This comment has been minimized.

Copy link
Owner Author

ChrisWellsWood commented Dec 10, 2016

@joshtriplett and @yohanesu75, thanks very much, I'll add these to the gist.

@ferrouswheel

This comment has been minimized.

Copy link

ferrouswheel commented Dec 10, 2016

Thanks for summarising the options!

For context there is an open issue for allowing this but it hasn't had any activity for almost two years.

@ChrisWellsWood

This comment has been minimized.

Copy link
Owner Author

ChrisWellsWood commented Dec 11, 2016

@ferrouswheel I don't mind not being able to implement an external trait on an external type, but maybe it should be highlighted in the Rust book so people think about default initialisation when they're learning about structs in general, so it's carried over to their applications on libraries. For a while I just assumed you could do it by default, until I tried!

@kstep

This comment has been minimized.

Copy link

kstep commented Dec 13, 2016

You can avoid making your Empty trait generic by using Self type:

trait Empty {
    fn empty() -> Self;
}
@rajasankar

This comment has been minimized.

Copy link

rajasankar commented Jun 5, 2019

Thanks for sharing the derive Default. I didnt know that before.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.