Rust Defaultについて調べてみた
Defaultとは?
名前の通り各タイプで実装されているdefault()
の値を返します。
例えば
fn main() {
let s = String::default();
println!("String: {}", s);
let int = i32::default();
println!("Integer: {}", int);
let str: &str = Default::default();
println!("&str: {}", str);
}
これを実行すると下記の様になります。
$ cargo run
String:
Integer: 0
&str:
それぞれのTypeにはdefault();
が実装されているので、{TypeName}::default()
で定義された値を返している。
各タイプに実装されているdefault()
の詳細は公式ドキュメントから確認できます。
例えばString
を見てみると 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()
}
}
#[derive(Default)] で struct にDefaultを設定
通常生成したオブジェクトに値を入れる時は下記の様な書き方になります。
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
上記の様にそれぞれ値を入れる必要があります。これでも良いのですが、実際のプロダクトではさらに多くの属性が含まれるため、いちいち値の入れるのはめんどくさいですね。
先程の Default::default();
を使ってみましょう。
fn main() {
let product : Product= Default::default();
println!("Name: {}, Price ${}", product.name, product.price);
}
Buildしてみると以下のようなエラーになります。
$ 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
これだと以下の様に怒られます。
理由はエラーでメッセージが出てる通り、 Product
タイプにはDefault
が実装されてないからです。
Product
にDefault
を使ってみましょう。
#[derive(Default)] // <- 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);
}
これを実行すると
% $ cargo run
Name: , Price $0
それぞれの String
とf64
に実装されているdefault()
が返す値が入ってます。
default()を実装してみる
自分で好きなデフォルト値を定義したいときはstruct
にdefault()
を実装するとできます。
// #[derive(Default)]は外す
struct Product {
name: String,
price: f64,
}
// 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(); <- 同じ結果を返す。
println!("Name: {}, Price ${}", product.name, product.price);
}
これを実行すると
$ cargo run
Name: Default name, Price $999
と、設定した値が入ります。
enumにもDefaultを設定できる
enumにもdafault
を定義することができます。
#[derive(Default, Debug)] // Debugは`println`でログを出すために必要
enum Fruit {
#[default]
Apple,
Banana,
Orange,
}
fn main() {
let fruit = Fruit::default();
println!("{:?}", fruit);
let banana = Fruit::Banana;
println!("{:?}", banana);
}
実行結果は下記になります。
$ cargo run
Apple
Banana
どんな使われ方してる?
実際にどんな使われ方をしてるか気になったので調べてみました。
例1: actix-webのheadタイプ
Rustのweb libraryで有名な actix-webをみてみましょう。
Requestには 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(),
}
}
}
中身を見てみると各タイプのdefault値を返してますね。
version
に関してはHTTP_11
がデフォルトで指定してるみたいです。
Reference:
まとめ
Default値の定義を struct
に実装することで、使用する時には実装されている default()
を実行してあげるだけなので、簡単ですね。
公式ドキュメント
pub trait Default: Sized {
fn default() -> Self;
}
Discussion