Bootstrap

rust 初探 -- struct

rust 初探 – struct

定义和实例化 struct

定义 struct

示例:

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

实例化 struct

  • 实例化的顺序可以不一样
  • 但是必须给所有字段都赋值
一些操作方法
  • 使用点标记法获取某个值:user1.email
  • 一旦 struct 的实例是可变的,那么里面所有的字段都是可变的
  • 字段初始化简写:当字段名和字段值对应变量名相同时,可以使用字段初始化简写
fn build_user(email: String, username: string) -> User {
	User {
		email,
		username,
		active: true,
		sign_in_count: 1,
	}
}
  • 当基于某个 struct 实例来创建一个新实例的时候,使用更新语法:
let user2 = User {
		email: String::from("user2_email"),
		username: String::from("user2_name"),
		active: use1.active,
		sign_in_count: use1.sign_in_count,
	};
let user2 = User {
		email: String::from("user2_email"),
		username: String::from("user2_name"),
		..user1
	};
  • tuple struct: struct Color(i32, i32),不同名的是不同的数据类型
  • Unit-Like Struct:没有任何字段的 struct,适用于需要在某个类型上实现某个 trait,但是在里面又没有想要存储的数据
struct 数据所有权
  • 如果 struct 里面没有存放引用,那么 struct 实例拥有其所有的数据,只要 struct 是有效的,那么里面的字段数据也是有效的
  • 如果存放了引用,那么就需要使用生命周期,生命周期保证只要 struct 实例是有效的,那么里面的引用也是有效的(否则报错)

struct 例子

// 这样子就可以未自定义的 struct 进行打印,因为自定义的没有实现 Display, Debug 的trait
#[derive(Debug)]
struct Rectangle {
    width: u32,
    length: u32,
}

fn main() {
    let rec = Rectangle {
        width: 20,
        length: 10,
    };

    println!("{}", area(&rec));
    println!("{:#?}", rec);
}

fn area(rec: &Rectangle) -> u32 {
    rec.length * rec.width
}

struct 方法

相关定义

  • 方法和函数类似:fn 关键字、名称、参数、返回值
  • 方法和函数不同之处:
    • 方法是在 struct 的上下文中定义
    • 第一个参数是 self,表示方法被调用的 struct 实例
  • 每个 struct 可以有多个 impl
#[derive(Debug)]
struct Rectangle {
    width: u32,
    length: u32,
}

// impl 声明块,在块里面定义方法
impl Rectangle {
    // 上下文
    // self 会自动推断为 Rectangle
    // 可以是 &self,也可以获得其所有权或可变借用
    // 这样会有更好的代码组织
    fn area(&self) -> u32 {
        self.length * self.width
    }
}

fn main() {
    let rec = Rectangle {
        width: 20,
        length: 10,
    };

    println!("{}", rec.area());
    println!("{:#?}", rec);
}

方法调用的运算符

  • Rust 会自动引用或者解引用:在调用方法的时候,会根据情况自动添加 &、&mut 或者 *,以便 object 可以匹配方法的签名
  • p1.distance(&p2); 等效 (&p1).distance(&p2);

方法参数

  • 除了 self,还可以添加其他参数,如 can_hold 函数:
#[derive(Debug)]
struct Rectangle {
    width: u32,
    length: u32,
}

// impl 声明块,在块里面定义方法
impl Rectangle {
    // 上下文
    // self 会自动推断为 Rectangle
    // 可以是 &self,也可以获得其所有权或可变借用
    // 这样会有更好的代码组织
    fn area(&self) -> u32 {
        self.length * self.width
    }

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

fn main() {
    let rec = Rectangle {
        width: 20,
        length: 10,
    };

    println!("{}", rec.area());
    println!("{:#?}", rec);

    let rec1 = Rectangle {
        width: 10,
        length: 5,
    };

    let rec2 = Rectangle {
        width: 30,
        length: 50,
    };

    println!("rec1 can hold: {}", rec.can_hold(&rec1));
    println!("rec2 can hold: {}", rec.can_hold(&rec2));
}

关联函数

  • 可以在 impl 块里定义不把 self 作为第一个参数的函数,它们叫做关联函数(不是方法)
  • 关联函数通常用于构造器
	#[derive(Debug)]
struct Rectangle {
    width: u32,
    length: u32,
}

// impl 声明块,在块里面定义方法
impl Rectangle {
    // 上下文
    // self 会自动推断为 Rectangle
    // 可以是 &self,也可以获得其所有权或可变借用
    // 这样会有更好的代码组织
    fn area(&self) -> u32 {
        self.length * self.width
    }

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

    fn square(size: u32) -> Rectangle {
        Rectangle {
            width: size,
            length: size,
        }
    }
}

fn main() {
    let s = Rectangle::square(3);
	// :: 符号:用于关联函数,或者模块创建的命名空间
    println!("{}", s.area());
    println!("{:#?}", s);

    let rec1 = Rectangle {
        width: 10,
        length: 5,
    };

    println!("s can hold: {}", rec1.can_hold(&s));
}
;