RustのstdにDefaultというのがある。
構造体のデフォルト値を決めることができる。
実用例からlibcoreでのどのような実装になっているかを見てみた。
traitはこんな感じ。
pub trait Default {
fn default() -> Self;
}
Example
Defaultなし
Defaultを利用しないとこんな感じでnew()
を作るようになる。struct SomeOptions { foo: i32, bar: f32, } impl SomeOptions { fn new() -> Self { Self { foo: 0, bar: 0.0f32 } } } fn main() { let options: SomeOptions = SomeOptions::new(); }
Defaultを使う
以下はドキュメントのコードです。#[derive]
アトリビュートでDefaultを使うことでDefault::default()
がSomeOptions::new()
の代わりになってnew()
を定義する必要がなくなりました!はい、便利。#[derive(Default)] struct SomeOptions { foo: i32, bar: f32, } fn main() { let options: SomeOptions = Default::default(); }
一部の値だけデフォルトを用いる
以下はドキュメントのコードです。
一部の値だけデフォルトの値を使うともできるfn main() { let options = SomeOptions { foo: 42, ..Default::default() }; }
Defaultを実装する
Defaultトレイトを自分で実装することももちろん可能。
ドキュメントではEnumを用いたコードを紹介していた。struct SomeOptions { foo: i32, bar: f32, } impl Default for SomeOptions { fn default() -> Self { Self { foo: 0, bar: 0.0f32 } } } fn main() { let options: SomeOptions = Default::default(); }
unwrap_or_default()
Result
やOption
ではunwrap_or_default()という関数が実装されている。
Optionを用いた例#[derive(Default, Debug, PartialEq)] struct SomeOptions { foo: i32, bar: f32, } fn main() { let options: SomeOptions = None.unwrap_or_default(); assert_eq!(options, Default::default()); }
実用例
gliumというOpenGLのラッパーではDrawParametersというでかい構造体がある。pub struct DrawParameters<'a> { pub depth: Depth, pub stencil: Stencil, pub blend: Blend, pub color_mask: (bool, bool, bool, bool), pub line_width: Option<f32>, pub point_size: Option<f32>, pub clip_planes_bitmask: u32, pub backface_culling: BackfaceCullingMode, pub polygon_mode: PolygonMode, pub multisampling: bool, pub dithering: bool, pub viewport: Option<Rect>, pub scissor: Option<Rect>, pub draw_primitives: bool, pub samples_passed_query: Option<SamplesQueryParam<'a>>, pub time_elapsed_query: Option<&'a TimeElapsedQuery>, pub primitives_generated_query: Option<&'a PrimitivesGeneratedQuery>, pub transform_feedback_primitives_written_query: Option<&'a TransformFeedbackPrimitivesWrittenQuery>, pub condition: Option<ConditionalRendering<'a>>, pub transform_feedback: Option<&'a TransformFeedbackSession<'a>>, pub smooth: Option<Smooth>, pub provoking_vertex: ProvokingVertex, pub primitive_bounding_box: (Range<f32>, Range<f32>, Range<f32>, Range<f32>), pub primitive_restart_index: bool, }
さすがにこれを毎回書くのはしんどいのでDefaultトレイトが実装されている。 Defaultトレイトを実装することでDefaultから変更したい箇所だけ書けばよくなる。
let params = glium::DrawParameters { point_size: Some(5.0), multisampling: false, ..Default::default() };
詳細
Rustのcoreライブラリではデフォルト値が決まっているものがある。 1.26時点では以下のようになっている。
type | default |
---|---|
bool | false |
char | ‘\x00’ |
usize | 0 |
u8 | 0 |
u16 | 0 |
u32 | 0 |
u64 | 0 |
u128 | 0 |
isize | 0 |
i8 | 0 |
i16 | 0 |
i32 | 0 |
i64 | 0 |
i128 | 0 |
f32 | 0 |
f64 | 0 |
libcoreの実装
1.26のDefaultのはdefault_imp!
というマクロを用いて実装されている。
実際の実装コードを見てもらうとがさっきの表の箇所が実装されているのがわかる。
macro_rules! default_impl {
($t:ty, $v:expr, $doc:tt) => {
#[stable(feature = "rust1", since = "1.0.0")]
impl Default for $t {
#[inline]
#[doc = $doc]
fn default() -> $t { $v }
}
}
}
default_impl! { bool, false, "Returns the default value of `false`" }
これ以外にもtuple
でも実装されていた。
他にもいっぱいあるみたいです。
追記(2018-05-29)
lo48576さんさんにこんなこともできるよと教えていただいたので追記。
その1
std::default::Default
はprelude経由で自動的にインポートされています。
Default::default()
の代わりにSomeOptions::default()
でできます。
Example1の改良
#[derive(Default)]
struct SomeOptions {
foo: i32,
bar: f32,
}
fn main() {
let options = SomeOptions::default();
}
その2
#[derive(Default)]
を用いた以下のようなコードはコンパイルが通りません。
Option
のデフォルトはNone
なのでNoDefault構造体
はDefault
を実装する必要がありません。
コンパイルを通するにはDefault
を実装します。
NG:
// Struct with no default impl.
struct NoDefault;
#[derive(Default)]
struct SomeOptions<Content> {
foo: Option<Content>,
bar: f32,
}
fn main() {
// ERROR!
// error[E0599]: no function or associated
// item named `default` found for type
// `SomeOptions<NoDefault>` in the
// current scope
let _ = SomeOptions::<NoDefault>::default(); // NG
}
OK:
// Struct with no default impl.
struct NoDefault;
struct SomeOptions<Content> {
foo: Option<Content>,
bar: f32,
}
impl<Content> Default for SomeOptions<Content> {
fn default() -> Self {
Self {
foo: Default::default(),
bar: Default::default(),
}
}
}
fn main() {
let _ = SomeOptions::<NoDefault>::default(); // OK
}
qnighyさんの記事やlo48576さんの記事 の記事が参考になると思います。