再论结构体与枚举
打印结构体
结构体前添加 #[derive(Debug)] ,使用{:?}打印出结构体的类型与字段列表。使用{:#?}打印出格式化的结构体的类型与字段列表。
例:使用{:?}打印出结构体。
#[derive(Debug)]
struct Point3d {
x: i32,
y: i32,
z: i32,
}
fn main() {
let p = Point3d {x:1,y:2,z:3};
println!("{:?}",p); // 打印:Point3d { x: 1, y: 2, z: 3 }
}
例:使用{:#?}打印出结构体。
#[derive(Debug)]
struct Point3d {
x: i32,
y: i32,
z: i32,
}
fn main() {
let p = Point3d {x:1,y:2,z:3};
println!("{:#?}",p);
}
/* 打印:
Point3d {
x: 1,
y: 2,
z: 3
}
*/
默认值
结构体前添加 #[derive(Debug)] ,则结构体会自动实现Default trait。
fn main() {
#[derive(Default)]
struct Point3d {
x: i32,
y: i32,
z: i32,
}
let po = Point3d::default();
println!("({},{},{})",po.x,po.y,po.z); // 输出 (0,0,0)
}
定义结构体的方法
struct不用实现trait也可以增加方法。
Rsut中struct的方法不定义在struct中,这与其他语言不同。使用impl
在struct结构体外为struct增添方法。
相比于Go,这种形式更加紧凑。
例:为Person实现方法。
struct Person {
name: String
}
impl Person {
fn new(n: &str) -> Person {
Person {
name: n.to_string(),
}
}
fn greeting(&self) {
println!("{} say hello .", self.name);
}
}
fn main() {
let peter = Person::new("Peter");
peter.greeting();
}
三种self与关联函数
self等价于表示调用函数的变量。
(1) self
若函数使用self作为第一个参数,会引发self所有权的move。然而这会有个很奇怪的问题,即"你调用了一下方法,然后你就不属于你了"。这很不合理。
使用Copy和Clone可以解决该问题,明显用法不被推荐。
例:以下代码编译错误。
struct A {
a: i32,
}
impl A{
fn add_one(self) -> i32{
self.a + 1
}
}
fn main(){
let a = A {a:1};
let result = a.add_one();
println!("{:?}",result);
}
(2) &self
&self
一般用于只读方法。
(3) &mut self
&mut self
一般用于可写方法。
可写方法只能被可变变量调用。不可变变量不能调用可写方法。
例:
// 结构体
#[derive(Debug)]
struct Point3d {
x: i32,
y: i32,
z: i32,
}
// 结构体方法
impl Point3d {
fn add_x(&mut self, x:i32) {
self.x += x;
}
}
fn main() {
// let p = Point3d {x:1,y:2,z:3}; // 若Point3d变量是不可变的则调用p.add_x(1);会无法通过编译。
let mut p = Point3d {x:1,y:2,z:3}; // 可变变量,可以调用可变方法。
p.add_x(1);
println!("{:?}",p);
}
注意:&self
的只读方法不可以调用&mut self
的可写方法,否则会编译出错。但&mut self
可写方法可以调用&self
只读方法。
这可以理解,从语义上说,只读方法中不应该调用可写方法,而可写方法允许调用只读方法。Rust在编译器层面做了这些限制。
(4)不使用self
不适用self的结构体方法相当于结构体的静态方法。这在Rust中被称为关联函数。
例如,String::from()方法。
关联函数通常被用于构造struct实例。
例:使用关联函数创建struct实例。
#[derive(Debug)]
struct Rect {
heigth:i32,
width:i32
}
impl Rect {
fn create(x:i32,y:i32) -> Rect {
Rect {heigth : x, width:y}
}
fn area(&self) -> i32 {
self.heigth * self.width
}
}
fn main() {
let r = Rect::create(10,20);
let area = r.area();
println!("{:?} , area = {}",r,area);
}
// 打印: Rect { heigth: 10, width: 20 } , area = 200
以下指示都可以通过所有权,绑定,借用等知识推导而来。这里总结一下,以增加对各种特性的理解。
结构体的可变与不可变
Rust不允许struct的字段为mut的。
#[derive(Debug)]
struct Point3d {
x: mut i32, // 编译不通过
y: i32,
z: i32,
}
必须是这样:
#[derive(Debug)]
struct Point3d {
x: i32,
y: i32,
z: i32,
}
改变struct字段需要将变量声明为mut
的。
fn main() {
let mut p = Point3d{x:1,y:2,z:3};
p.x += 10;
println!("{:?}",p); // 打印:Point3d { x: 11, y: 2, z: 3 }
}
struct的mut
变量,可对变量的资源作整体替换。
fn main() {
let mut p = Point3d{x:1,y:2,z:3};
p = Point3d { x: 3, y: 2, z: 1 };
println!("{:?}",p); // 打印:Point3d { x: 3, y: 2, z: 1 }
}
结构体的可变借用
可变借用可以直接对字段进行修改。
fn main() {
let mut p = Point3d{x:1,y:2,z:3};
let p1 = &mut p;
p1.x += 10; // 可以看到,
println!("{:?}",p1); // 变量的可变借用与原变量拥有一样的行为方式。
}
需要使用*
才可以把变量重新替换为新资源。
fn main() {
let mut p = Point3d{x:1,y:2,z:3};
let p1 = &mut p;
*p1 = Point3d { x: 3, y: 2, z: 1 }; // 绑定新资源!
println!("{:?}",p1); // 打印:Point3d { x: 3, y: 2, z: 1 }
}
绑定新资源后,原变量也会被改变。
fn main() {
let mut p = Point3d{x:1,y:2,z:3};
{
let p1 = &mut p;
*p1 = Point3d { x: 3, y: 2, z: 1 };
} // 需要释放可变借用,否则编译不通过。若存在可变借用,原始变量不可被访问。
println!("{:?}",p); // 打印:Point3d { x: 3, y: 2, z: 1 }
}