rust学习-结构体

结构体和元组的区别

结构体需要命名各部分数据以便能清楚的表明其值的意义
结构体比元组更灵活:不需要依赖顺序来指定/访问实例中的值

结构体

// 整个实例可变;Rust 不允许只将某个字段标记为可变
struct User {
    active: bool,
    username: String,
    email: String,
    sign_in_count: u64,
}

let mut user1 = User {
	// 使用自身拥有所有权的 String 类型,而不是 &str 字符串 slice 类型
	// 想要这个结构体拥有它所有的数据
    email: String::from("[email protected]"),
    username: String::from("someusername123"),
    active: true,
    sign_in_count: 1,
};

user1.email = String::from("[email protected]");

变量与字段同名时的间写

fn build_user(email: String, username: String) -> User {
    User {
        email,
        username,
        active: true,
        sign_in_count: 1,
    }
}

依据其他实例创建实例

fn main() {
    // --snip--
	
	// 设置一个新的 email 值,其余值来自 user1 变量中实例的字段
    let user2 = User {
        email: String::from("[email protected]"),
        // 创建 user2 后不能再使用 user1
        // user1 的 username 字段中的 String 被移到 user2
        // 如果给 user2 的 email 和 username 赋予新 String 值,只使用 user1 的 active 和 sign_in_count 值,
        // 则user1 在创建 user2 后仍然有效
        ..user1
    };
}

元组结构体

没有具体的字段名,只有字段的类型,但有名称,注意元组没名称
元组结构体实例类似于元组:
(1)可以将其解构为单独部分
(2)可以使用 . 后跟索引来访问单独的值

struct Color(i32, i32, i32);
struct Point(i32, i32, i32);

fn main() {
    let black = Color(0, 0, 0);
    let origin = Point(0, 0, 0);
}

单元结构体

要在某个类型上实现 trait 但不需要在类型中存储数据
不需要花括号或圆括号
用于特性trait

struct AlwaysEqual;
fn main() {
	// 在 subject 变量中获得 AlwaysEqual 的实例
    let subject = AlwaysEqual;
}

结构体存储被其他对象拥有的数据的引用

错误案例:missing lifetime specifier,丢失生命期标识符

struct User {
    active: bool,
    username: &str,
    email: &str,
    sign_in_count: u64,
}

fn main() {
    let user1 = User {
    	// 在结构体中存储一个引用而不指定生命周期,无效
        email: "[email protected]",
        username: "someusername123",
        active: true,
        sign_in_count: 1,
    };
}

元组和结构体的对比

元组缺点:其他模块使用方这么知道第一个元素是宽还是长

fn main() {
    let rect1 = (30, 50);

    println!(
        "The area of the rectangle is {} square pixels.",
        area(rect1)
    );
}

fn area(dimensions: (u32, u32)) -> u32 {
    dimensions.0 * dimensions.1
}

结构体解决了上述问题

struct Rectangle {
    width: u32,
    height: u32,
}

fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };

    println!(
        "The area of the rectangle is {} square pixels.",
        area(&rect1)
    );
}

fn area(rectangle: &Rectangle) -> u32 {
    rectangle.width * rectangle.height
}

结构体的打印

Debug trait

// 为结构体显式选择打印调试信息的功能
#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };

    println!("rect1 is {:?}", rect1);   // 平铺打印
    println!("rect1 is {:#?}", rect1); // 类似json格式化的打印
	
	// 更优雅的做法
	// 不希望 dbg! 拥有 rect1 的所有权
	dbg!(&rect1);
}

方法

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

// impl 块中的所有内容都将与 Rectangle 类型相关联
// Self 类型是 impl 块的类型的别名,注意Self和self区别
impl Rectangle {
    // 在结构体的上下文中被定义
    // &self 实际上是 self: &Self 的缩写
    // 方法可以选择获得 self 的所有权,这里一看就是没有选择
    // 如果想在方法中改变调用方法的实例,需将第一个参数改为 &mut self
    fn area(&self) -> u32 {
        self.width * self.height
    }
}

fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };

    println!(
        "The area of the rectangle is {} square pixels.",
        rect1.area()
    );
}

方法的名称与结构中的字段相同

impl Rectangle {
    // 通常,与字段同名的方法将被定义为只返回字段中的值
    fn width(&self) -> bool {
        self.width > 0
    }
}

fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };

    // 在同名方法中使用一个字段来达到任何目的
    // 在 rect1.width 后面加上括号时,Rust 知道指的是方法 width
    // 当不使用圆括号时,Rust 知道指的是字段 width
    if rect1.width() {
        println!("The rectangle has a nonzero width; it is {}", rect1.width);
    }
}

方法的多参

impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }

	// self 是否能包裹other
	// 获取另一个 Rectangle 的不可变借用作为参数
    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }
}

关联函数

所有在 impl 块中定义的函数被称为关联函数(associated function)
定义不以 self 为第一参数的关联函数不叫方法
它们不作用于一个结构体的实例,而是直接作用于类型上,比如 String::from 函数,在 String 类型上定义
关联函数经常被用作返回一个结构体新实例的构造函数

impl Rectangle {
    // 定义一个正方形
    fn square(size: u32) -> Rectangle {
        Rectangle {
            width: size,
            height: size,
        }
    }
}

使用结构体名和 :: 语法来调用这个关联函数:

let sq = Rectangle::square(3);

多impl

impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}

impl Rectangle {
    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }
}

和C++的区别

C/C++ 语言中,有两个不同的运算符来调用方法:
(1). 直接在对象上调用方法,
(2)-> 在一个对象的指针上调用方法,先解引用(dereference)指针

Rust 并没有一个与 -> 等效的运算符
Rust 有一个叫 自动引用和解引用(automatic referencing and dereferencing)的功能(方法调用中有用到):
当使用 object.something() 调用方法时,Rust 会自动为 object 添加 &、&mut 或 *,以便使 object 与方法签名匹配

等价代码:

p1.distance(&p2);
(&p1).distance(&p2);

这种自动引用的行为之所以有效,是因为方法有一个明确的接收者:self 的类型。
在给出接收者和方法名的前提下,Rust 可以明确地计算出方法:
仅读取(&self),做出修改(&mut self)或者是获取所有权(self)

Rust 对方法接收者的隐式借用,让使用所有权的人更少顾虑

你可能感兴趣的:(rust,rust,学习,开发语言)