DEV Community

Yuki Ishii
Yuki Ishii

Posted on

What is Default in Rust?

What is Default?

Each types returns default values from default().

For example

fn main() {
    let s = String::default();
    println!("String: {}", s);

    let int = i32::default();
    println!("Integer: {}", int);

    let str: &str = Default::default();
    println!("&str: {}", str);
}
Enter fullscreen mode Exit fullscreen mode

Run this code

$ cargo run

String: 
Integer: 0
&str: 
Enter fullscreen mode Exit fullscreen mode

Each type implements default(), so the value defined by {TypeName}::default() is returned.

Details of default() implemented for each type can be found in the official documentation.
For example, if you look at String, it says it returns String::new().

#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_default_impls", issue = "87864")]
impl const Default for String {
    /// Creates an empty `String`.
    #[inline]
    fn default() -> String {
        String::new()
    }
}
Enter fullscreen mode Exit fullscreen mode

https://doc.rust-lang.org/src/alloc/string.rs.html#2263

Use #[derive(Default)] to implement Default

Normally, when inserting a value into a generated object, it is written as follows.

struct Product {
    name: String,
    price: f64,
}

fn main() {
    let product = Product {
        name: "".to_string(),
        price: 0.0,
    };
    println!("{} price ${}", product.name, product.price);
}

# $ cargo run
# Name: , Price $0
Enter fullscreen mode Exit fullscreen mode

You need to put in a value for each of them as shown above. This would be fine, but in an actual product, there will be more attributes, so it is a pain to put in a value for one by one.

Let's use Default::default(); as described above.

fn main() {
    let product : Product= Default::default();
    println!("Name: {}, Price ${}", product.name, product.price);
}
Enter fullscreen mode Exit fullscreen mode

Building returns following error.

$ cargo run
   Compiling app v0.1.0 (/Users/s14960/ghq/github.com/yuki0418/rust-practice/app)
error[E0277]: the trait bound `Product: Default` is not satisfied
 --> src/main.rs:7:28
  |
7 |     let product : Product= Default::default();
  |                            ^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `Product`
  |
help: consider annotating `Product` with `#[derive(Default)]`
  |
1 | #[derive(Default)]
  |

For more information about this error, try `rustc --explain E0277`.
error: could not compile `app` due to previous error
Enter fullscreen mode Exit fullscreen mode

As you can see, the reason of this error is that Default is not implemented for Product.

Let implement Default to Product.

#[derive(Default)] // <- Use Default.
struct Product {
    name: String,
    price: f64,
}

fn main() {
    let product : Product= Default::default();
    println!("Name: {}, Price ${}", product.name, product.price);

    let named_product = Product {
        name: "Coffee".to_string(),
        ..Default::default()
    };
    println!("Name: {}, Price ${}", named_product.name, named_product.price);
}
Enter fullscreen mode Exit fullscreen mode

Then run this code.

% $ cargo run
Name: , Price $0
Enter fullscreen mode Exit fullscreen mode

It contains the value returned by default() implemented in each String and f64.

Implement default() to Product

You can define a custom default value with implementing default() to a struct.

// Remove #[derive(Default)]
struct Product {
    name: String,
    price: f64,
}

// Implement Default
impl Default for Product {
    fn default() -> Self {
        Self {
            name: "Default name".to_string(),
            price: 999.0,
        }
    }
}

fn main() {
    let product= Product::default();
    // let product : Product = Default::default(); <- Return same result 
    println!("Name: {}, Price ${}", product.name, product.price);
}
Enter fullscreen mode Exit fullscreen mode

Run this code.

$ cargo run
Name: Default name, Price $999
Enter fullscreen mode Exit fullscreen mode

It returns default values which just implemented before.

Implement Default to enum

You can implement default to enum as well.

#[derive(Default, Debug)] // Need Debug for `println` for logging.
enum Fruit {
    #[default]
    Apple,
    Banana,
    Orange,
}

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

    let banana = Fruit::Banana;
    println!("{:?}", banana);
}
Enter fullscreen mode Exit fullscreen mode

The result is below.

$ cargo run
Apple
Banana
Enter fullscreen mode Exit fullscreen mode

How be used in real product

Let check how Default is used in real life products.

actix-web head type

Let check actix-web which is poplar library for web in Rust.
Request has RequestHead.

#[derive(Debug, Clone)]
pub struct RequestHead {
    pub method: Method,
    pub uri: Uri,
    pub version: Version,
    pub headers: HeaderMap,
    pub peer_addr: Option<net::SocketAddr>,
    flags: Flags,
}

impl Default for RequestHead {
    fn default() -> RequestHead {
        RequestHead {
            method: Method::default(),
            uri: Uri::default(),
            version: Version::HTTP_11,
            headers: HeaderMap::with_capacity(16),
            peer_addr: None,
            flags: Flags::empty(),
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

As you can see, it returns default values for each types.
HTTP_11 is defined for version as the default value.

Reference:
https://github.com/actix/actix-web/blob/3200de3f34b21f65bf84d7b04ba118f03d808f02/actix-http/src/requests/head.rs

Conclusion

It is easy to implement the definition of the Default value in a struct so that when you use it, all you have to do is execute the implemented default().

Official document

https://doc.rust-lang.org/std/default/trait.Default.html

pub trait Default: Sized {
    fn default() -> Self;
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)