再论结构体与枚举

打印结构体

结构体前添加 #[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 }
}

results matching ""

    No results matching ""