Skip to content

定义结构体并实例化 ​

类似面向对象编程的类语法

rust
struct Student {
    stu_no: i32,
    name: String,
    register_at: String
}

fn main() {
    let tom = Student {
        stu_no: 1001,
        name: String::from("tom"),
        register_at: String::from("2020-01-11"),
    };
    println!("stu_no: {}", tom.stu_no);
    println!("name: {}", tom.name);
    println!("register: {}", tom.register_at);
}

字段初始化的简写语法 ​

类似js对象的简写语法,key和value变量同名可以省略

rust
struct Student {
    stu_no: i32,
    name: String,
    register_at: String
}

fn build_student(id:i32, name:String, register_at:String) -> Student {
    Student {
        stu_no: id, // 不同名是不能缩写语法的
        name,
        register_at,
    }
}

fn main() {
    let name = String::from("jerry");
    let reg_at = String::from("2022-11-21");
    let jerry = build_student(1002, name, reg_at);
    println!("stu_no: {}", jerry.stu_no);
    println!("name: {}", jerry.name);
    println!("register: {}", jerry.register_at);
}

使用结构体更新语法从其他实例创建实例 ​

这个功能类似js的延展符的功能 {...obj1, ...obj2}

rust
struct User {
    id: i32,
    name: String,
    password: String,
    is_actived: bool,
    is_banned: bool,
    is_real_auth: bool,
}
fn main() {
    let tom = User {
        id: 1001,
        name: String::from("tom"),
        password: String::from("123456"),
        is_actived: true,
        is_banned: false,
        is_real_auth: true,
    };
    let jerry = User {
        id: 1002,
        name: String::from("jerry"),
        ..tom // 不会覆盖已有的字段
    };
    println!("jerry.id        :{}", jerry.id);
    println!("jerry.name      :{}", jerry.name);
    println!("jerry.password  :{}", jerry.password);
    println!("jerry.is_actived:{}", jerry.is_actived);
    println!("jerry.is_banned :{}", jerry.is_banned);
    println!("jerry.is_banned :{}", jerry.is_real_auth);
}

定义类元组结构体 ​

rust
// 类似元组一样的结构体
struct IpAddrV4(u8, u8, u8, u8);

impl IpAddrV4 {
    fn to_addr_string(&self) -> String {
        let mut addr_str = String::new();
        addr_str += self.0.to_string().as_str();
        addr_str += ".";
        addr_str += self.1.to_string().as_str();
        addr_str += ".";
        addr_str += self.2.to_string().as_str();
        addr_str += ".";
        addr_str += self.3.to_string().as_str();
        addr_str
    }
}

fn main() {
    let ip = IpAddrV4(192, 168, 11, 255);
    println!("ip: {}", ip.to_addr_string());
}

使用#[derive(Debug)] ​

  1. 不用管 trait 和 #[derive(debug)] 是什么, 后续都会学
  2. 只需要知道, 在结构体上加上这个就可以使用 println! 这个宏来输出, 以便查看信息
rust
#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

fn main() {
    let rect = Rectangle {
        width: 10,
        height: 10
    };
    println!("rect width : {}", rect.width);
    println!("rect height: {}", rect.height);

    // 如果没有加上 #[derive] 无法直接输出
    println!("rect: {:?}", rect);

    // 也可以使用dbg宏
    dbg!(rect);
}

实例方法 ​

类似面向对象编程的定义方法

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

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

    // 多个参数  &Self 就是 &Rectangle 的意思,
    // 只不过 Self 是关键字代表当前的 struct
    fn can_hold(&self, other_rect: &Self) -> bool {
        self.width >= other_rect.width && self.height >= other_rect.height
    }
}

fn main() {
    let rect = Rectangle {
        width: 10,
        height: 10
    };
    let rect2 = Rectangle {
        width: 20,
        height: 20,
    };

    println!("rect area: {}", rect.get_area()); // 100
    println!("rect2 area: {}", rect2.get_area()); // 100

    println!("rect2 can hold rect? {}", rect2.can_hold(&rect));
}

关联函数 ​

关联函数只是返回值是 Rectangle, 但第一个参数并不是 &self 所以不能称之为方法

比如之前一直使用的 String::from 就是一个关联函数, 他的第一个参数并不是 &self

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

impl Rectangle {
    // 有些类似面向对象编程的静态方法
    fn square(size: u32) -> Self {
        Self {
            width: size,
            height: size,
        }
    }
}

fn main() {
    let square = Rectangle::square(10);
    dbg!(square);
}

多个 impl ​

一个结构体可以有多个 impl 块

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

// 放实例方法
impl Rectangle {
    fn get_area(&self) -> u32 {
        self.width * self.height
    }

    fn can_hold(&self, other_rect: &Self) -> bool {
        self.width >= other_rect.width && self.height >= other_rect.height
    }
}

// 放关联函数
impl Rectangle {
    fn square(size: u32) -> Self {
        Self {
            width: size,
            height: size,
        }
    }
}

fn main() {

    let square = Rectangle::square(10);
    dbg!(square);
}

self 和 Self ​

可以这样理解 self 代表当前实例, 作用类似 Java 的 this 关键字

而 Self 代表当前 impl 的 struct, 如: impl X Self 就代表 X

小例子: 使用结构体语法改造猜数字游戏代码 ​

rust
use rand::random_range;
use std::cmp::Ordering;
use std::io;

// 将控制游戏逻辑的字段放到一个结构体中
struct GuessNumberConfig {
    is_debug_mode: bool,
    anwser_number: u32,
    input_content: String,
    guess_chances: u32,
    min_number: u32,
    max_number: u32,
}

// 实现一些配置字段的方法
impl GuessNumberConfig {
    fn new() -> Self {
        Self {
            is_debug_mode: false,
            anwser_number: 0,
            input_content: String::new(),
            guess_chances: 1,
            min_number: 1,
            max_number: 10,
        }
    }

    // 设置调试模式
    fn set_debug_mode(&mut self, is_debug_mode: bool) {
        self.is_debug_mode = is_debug_mode;
    }

    // 设置答案随机数
    fn set_anwser_number(&mut self) {
        self.anwser_number = random_range(self.min_number..=self.max_number);
    }

    // 设置输入内容
    fn set_input_content(&mut self, input_str: String) {
        self.input_content = input_str;
    }

    // 解析输入内容为数字
    fn parse_input_content(&self) -> u32 {
        let num: u32 = self
            .input_content
            .clone()
            .trim()
            .parse()
            .expect("failed to parse input content");
        num
    }

    // 设置猜测机会
    fn set_guess_chances(&mut self, guess_chances: u32) {
        self.guess_chances = guess_chances;
    }

    // 是否还有猜测机会
    fn has_guess_chances(&self) -> bool {
        self.guess_chances > 0
    }

    // 减少猜测机会
    fn decrease_guess_chances(&mut self) {
        self.set_guess_chances(self.guess_chances - 1);
    }

    // 设置答案随机数最小数字
    fn set_min_number(&mut self, min_num: u32) {
        self.min_number = min_num;
    }

    // 设置答案随机数最大数字
    fn set_max_number(&mut self, max_num: u32) {
        self.max_number = max_num;
    }
}

fn main() {
    println!("=== 猜数字游戏 ===");

    let mut config = GuessNumberConfig::new();
    config.set_debug_mode(true);
    config.set_guess_chances(3);
    config.set_anwser_number();
    config.set_min_number(1);
    config.set_max_number(9);

    // 是否输出调试信息(告知答案)
    if config.is_debug_mode {
        println!("答案数字是:{}", config.anwser_number);
    }

    loop {
        // 判断猜测机会
        if !config.has_guess_chances() {
            println!("机会用完了, 游戏结束,答案是:{}", config.anwser_number);
            break;
        }

        // 输出提示信息
        print!(
            "请输入一个数字({}-{}):",
            config.min_number, config.max_number
        );
        println!(",你还有{}次机会", config.guess_chances);

        // 获取用户输入并设置到结构体字段中
        let mut input_str = String::new();
        io::stdin().read_line(&mut input_str).expect("fail to read");
        config.set_input_content(input_str);

        // 解析输入内容为数字并判断大小
        let input_num = config.parse_input_content();
        match config.anwser_number.cmp(&input_num) {
            Ordering::Less => println!("你输入的数字太小了"),
            Ordering::Greater => println!("你输入的数字太大了"),
            Ordering::Equal => {
                println!("恭喜你猜对了, 你赢了 ^_^ ");
                break;
            }
        }

        // 设置猜测机会(减少一次)
        config.decrease_guess_chances();
    }
}

Released under the MIT License.