201

In the following example, I would much prefer to assign a value to each field in the struct in the declaration of the fields. Alternatively, it effectively takes one additional statement for each field to assign a value to the fields. All I want to be able to do is to assign default values when the struct is instantiated.

Is there a more succinct way of doing this?

struct cParams {
    iInsertMax: i64,
    iUpdateMax: i64,
    iDeleteMax: i64,
    iInstanceMax: i64,
    tFirstInstance: bool,
    tCreateTables: bool,
    tContinue: bool,
}

impl cParams {
    fn new() -> cParams {
        cParams {
            iInsertMax: -1,
            iUpdateMax: -1,
            iDeleteMax: -1,
            iInstanceMax: -1,
            tFirstInstance: false,
            tCreateTables: false,
            tContinue: false,
        }
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Brian Oh
  • 9,604
  • 12
  • 49
  • 68

2 Answers2

287

You can provide default values for your struct by implementing the Default trait. The default function would look like your current new function:

impl Default for cParams {
    fn default() -> cParams {
        cParams {
            iInsertMax: -1,
            iUpdateMax: -1,
            iDeleteMax: -1,
            iInstanceMax: -1,
            tFirstInstance: false,
            tCreateTables: false,
            tContinue: false,
        }
    }
}

You can then instantiate the struct by giving only the non-default values:

let p = cParams { iInsertMax: 10, ..Default::default() };

With some minor changes to your data structure, you can take advantage of an automatically derived default implementation. If you use #[derive(Default)] on a data structure, the compiler will automatically create a default function for you that fills each field with its default value. The default boolean value is false, the default integral value is 0.

An integer's default value being 0 is a problem here since you want the integer fields to be -1 by default. You could define a new type that implements a default value of -1 and use that instead of i64 in your struct. (I haven't tested that, but it should work).

However, I'd suggest to slightly change your data structure and use Option<i64> instead of i64. I don't know the context of your code, but it looks like you're using the special value of -1 to represent the special meaning "infinite", or "there's no max". In Rust, we use an Option to represent an optionally present value. There's no need for a -1 hack. An option can be either None or Some(x) where x would be your i64 here. It might even be an unsigned integer if -1 was the only negative value. The default Option value is None, so with the proposed changes, your code could look like this:

#[derive(Default)]
struct cParams {
    iInsertMax: Option<u64>,
    iUpdateMax: Option<u64>,
    iDeleteMax: Option<u64>,
    iInstanceMax: Option<u64>,
    tFirstInstance: bool,
    tCreateTables: bool,
    tContinue: bool,
}

let p = cParams { iInsertMax: Some(10), ..Default::default() };
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Zargony
  • 9,615
  • 3
  • 44
  • 44
  • 2
    Thanks, I had a quick read, but I'll re-read to better-understand. The "natural" defaults that some languages use such as I believe zero, false, "", etc., would suit me. I do understand that there are wider implications than my small "problem" to solve. Ability to state eg. "iVal : i64 = 0", would solve my wider needs, but I guess that's not going to happen. The "#[deriving(Default)]" should solve most of my wants. I'm unsure why I used -1 in my test program, but it's not needed (historic). It would be very useful (IMHO) to be able to assign a value in situ where the field is defined. – Brian Oh Oct 29 '13 at 10:10
  • 17
    @BrianOh, tangentially, the "default values for struct fields" (i.e. something like `struct Foo { val: i64 = 0 }`) has been proposed and so may appear in later versions. – huon Oct 29 '13 at 12:14
  • It would be good if that were implemented IMO - "struct foo {....". I made the changes as suggested by you, using the struct as written in my question and with the default. That certainly suits me better and is far more concise. Being unfamiliar with the syntax, one minor problem that I had was not knowing the syntax for ALL defaults. IE: I used "= cParams{iInsertMax : 10, ..Default::default()};", but I actually want "iInstanceMax" to also be a default. IMO it would be preferable for "#[deriving(Default)]" to be part of the struct, but I guess the alternative suits the compiler better. – Brian Oh Oct 29 '13 at 14:37
  • 1
    To instantiate a struct with all defaults, try `let p: cParams = Default::default();`. Wether you implement the `Default` trait by yourself or let the compiler implement it by using `#[deriving(Default)]` should make no difference in how it is used. – Zargony Oct 29 '13 at 21:20
  • 3
    Many thanks for that. IMHO the defaults should be the default. IE. I don't think it should be necessary to specify Default:default etc., etc. I also think that the fields should be able to be assigned a value where they are defined. That's just from my simple perspective, and I realize that Rust is designed to be safe, and that there is a much wider perspective than mine. When one is learning the language (or at least me), the current implementation seems a little cumbersome. Rust is not a simple language IMHO, and the more that can feasibly be done to simplify it the better for me at least. – Brian Oh Oct 29 '13 at 22:17
  • @BrianOh: If you use `#[deriving(Default)]` then everything is defaulted for you. The only use of writing `Default::default()` manually is to specialize it for your case. So... the default is already the default. – Matthieu M. Feb 18 '15 at 14:37
  • I'd like to provide a default (null) value for a *mut u8 in a struct that I'm passing to FFI - know how to do that - it seems that it cannot be derived like the others. – Andrew Mackenzie Aug 20 '16 at 16:01
  • 5
    Is it necessary to define defaults for all fields when implementing `Default` for a struct? – stevensonmt Jul 06 '18 at 03:35
  • 1
    As @huon said, there are proposals to make this simpler with syntax simuilar to Python https://internals.rust-lang.org/t/struct-field-defaults/3412 – James McCorrie Jan 04 '21 at 13:43
  • Thank you for your answer @Zargony. As a Rust noob I was looking for how to do this and got as far as the `Default` docs but they fail to explain what (if any) values it provides as defaults https://doc.rust-lang.org/std/default/trait.Default.html – Anentropic Apr 02 '21 at 09:48
  • Please note, using `Option<>` instead of `-1` for integer types has memory implications, in particular the size of the original struct with just `u64`s is `40` bytes, while the size of the struct with `Option`s is `72` bytes, ~2 times more... :( – Andriy Berestovskyy Mar 07 '22 at 07:21
  • It seems that doesn't work with private fields sadly, which makes it non-resistant to any change where private fields are added. – RedGlyph Sep 19 '22 at 12:13
36

nowadays, it is possible to do exactly what OP was asking 8 years ago

to assign a value to each field in the struct in the declaration of the fields.

using derivative crate:

#![allow(non_snake_case)]
#![allow(non_camel_case_types)]

use derivative::Derivative;

#[derive(Derivative)]
#[derivative(Debug, Default)]
struct cParams {
    #[derivative(Default(value = "-1"))]
    iInsertMax: i64,
    #[derivative(Default(value = "-1"))]
    iUpdateMax: i64,
    #[derivative(Default(value = "-1"))]
    iDeleteMax: i64,
    #[derivative(Default(value = "-1"))]
    iInstanceMax: i64,
    #[derivative(Default(value = "false"))]
    tFirstInstance: bool,
    #[derivative(Default(value = "false"))]
    tCreateTables: bool,
    #[derivative(Default(value = "false"))]
    tContinue: bool,
}

fn main() {
    println!("cParams: {:?}", cParams::default());
}

Also, it is not necessary to define default falsy values for booleans in this way because Default will set them to this value anyway

SET001
  • 11,480
  • 6
  • 52
  • 76
  • 19
    It would be nice to see something like that in the standard library one day. – at54321 Dec 15 '21 at 07:08
  • 19
    @at54321 Hopefully, not, at least not with this syntax. This code looks so ugly and verbose (in my opinion). If one has to do this, then it's a lot better just to implement `Default`. No need to clutter the language with this stuff, otherwise soon Rust would look like C++, which would be a terrible idea. It's better it remains as a crate and people use it if they want it. – nbro Dec 25 '22 at 13:23