Rust中没有面向对象中的构造函数的概念,取而代之,通常情况下在初始化一个变量时就要为其所有的字段赋值!
有的时候这样比较麻烦,可以使用 Default 和 With Trait 来简化初始化操作;
源代码:
Rust中的默认初始化和初始化重载 引言 与 Go 中默认给出默认值不同,Rust 要求在创建对象的时候就对各个字段进行初始化;
例如,下面的代码没有对字段进行初始化,无法编译:
1 2 3 4 5 6 7 8 9 10 11 12 #[derive(Debug)] pub struct Foo { bar: String , baz: i32 , abc: bool , } fn main () { let x = Foo {}; println! ("{:?}" , x); }
报错:
1 2 3 4 5 error[E0063]: missing fields `abc`, `bar` and `baz` in initializer of `Foo` --> examples/0_default.rs:8:13 | 8 | let x = Foo {}; | ^^^ missing `abc`, `bar` and `baz`
因此,我们要手动赋值:
1 2 3 4 5 let x = Foo { bar: "" .to_string (), baz: 0 , abc: false };
但是每次都需要手动的指定空值,非常麻烦;
我们可以使用 Default Trait 来简化;
Default Trait 我们可以为 Foo 类型实现 Default Trait:
examples/0_default.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 impl Default for Foo { fn default () -> Self { Foo { bar: "" .to_string (), baz: 0 , abc: false , } } } fn main () { let x = Foo::default (); println! ("{:?}" , x); }
随后即可使用 Foo::default() 初始化;
同时,也可以初始化部分字段,例如:
1 let y = Foo { baz: 2 , ..Default ::default () };
实际上,对于 Rust 中的常见类型,他们默认都实现了 Default Trait;
因此我们可以直接使用 #[derive(Default)] 来生成 Default Trait,而无需手动实现;
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #[derive(Debug, Default)] pub struct Foo { bar: String , baz: i32 , abc: bool , } fn main () { let x = Foo::default (); let y = Foo { baz: 2 , ..Default ::default () }; println! ("{:?}" , x); println! ("{:?}" , y); }
With Trait 在面向对象的语言中,可以通过单个参数或多个参数构造一个新的对象;
除了上面 Default 的方式外,还可以通过 With Trait 实现类似的功能;
例如:
examples/1_with.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 pub trait With <T> { fn with (value: T) -> Self ; } #[derive(Debug, Default)] pub struct Foo { bar: String , baz: i32 , abc: bool , } impl With <String > for Foo { fn with (x: String ) -> Self { Foo { bar: x, ..Default ::default () } } } impl With <i32 > for Foo { fn with (x: i32 ) -> Self { Foo { baz: x, ..Default ::default () } } } impl With <bool > for Foo { fn with (x: bool ) -> Self { Foo { abc: x, ..Default ::default () } } } impl With <(String , bool )> for Foo { fn with (x: (String , bool )) -> Self { Foo { bar: x.0 , abc: x.1 , ..Default ::default () } } }
在上面的代码中,我们定义的 With Trait:
1 2 3 pub trait With <T> { fn with (value: T) -> Self ; }
我们分别为 Foo 类型实现了不同范型类型的 With:String、i32、bool 甚至 (String, bool) 类型;
因此,我们可以使用 with 函数:
examples/1_with.rs
1 2 3 4 5 6 7 8 9 10 11 fn main () { let a = Foo::with ("test" .to_string ()); let b = Foo::with (1 ); let c = Foo::with (true ); let d = Foo::with (("multi" .to_string (), true )); println! ("a: {:?}" , a); println! ("b: {:?}" , b); println! ("c: {:?}" , c); println! ("d: {:?}" , d); }
注意到,上面调用的都是 Foo::with 方法,但是实际上是不同的 Trait 范型实现!
虽然 Rust 中没有范型,但是我们可以通过 Trait + Generic 的方式实现相同的功能!
..运算符最后,再补充一点,在上面的 ..Default::default() 会将对象各个字段解构,随后赋给对应字段名相同的属性;
除了 default 构建的对象,正常的对象也可以使用这个运算符结构,例如:
examples/2_dot_operator.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #[derive(Debug)] pub struct Foo { bar: String , baz: i32 , abc: bool , } fn main () { let x = Foo { bar: "hello" .to_string (), baz: 0 , abc: false , }; let y = Foo { abc: true , ..x }; println! ("{:?}" , y); }
我们可以使用 ..x 来构造 y;
但是需要注意的是,.. 也是 Move 语义,因此上面的代码如果在后面使用了x,则会报错!
附录 源代码:
参考文章: