Rust 入门笔记【三】

结构体

这是一个结构体的简单例子。

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

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

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

    println!("{}",area(&rect1));
}

下面这种情况,类似移动构造

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

fn main() {
    let user1 = User {
        name: String::from("alice"),
        active: true,
        email: String::from("eamil@example.com"),
        sign_in_count: 0,
    };

    let user2 = User {
        name: String::from("Bob"),
        ..user1
    };
    println!("{}", user1.name);
    println!("{}", user2.name);

    println!("{}", user1.email);
    println!("{}", user2.email);
}

这里创建了两个对象,其中user2只有nameuser1不同。但是要注意的是user1中的其他变量都被移动到了user2中,因此如果尝试访问其他变量就会出错。如果我们需要拷贝可以这样写。

	let user2 = User {
        name: String::from("Bob"),
        email:user1.email.clone(),
        ..user1
    };

成员函数

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

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

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

    println!("{}", rect1.area());
}

这样就是一个成员函数的例子,如果有成员函数中还要传入其他的对象可以这样写

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

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

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

    let rect2 = Rectangle {
        width: 10,
        height: 40,
    };

    println!("{}", rect1.can_hold(&rect2));
    println!("{}", rect2.can_hold(&rect1));
}

关联函数

所有在impl块中定义的函数都被称为关联函数,但并不一定要关联self,比如String::from。在rust中是没有默认构造函数的,我们可以自己实现一个关联函数来当作构造函数使用,这样的函数一般命名为new

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

impl Rectangle {
    fn new(width: u32, height: u32) -> Self {
        Self { width, height }
    }
    fn square(size: u32) -> Self {
        Self {
            width: size,
            height: size,
        }
    }
}

fn main() {
    let rect1 = Rectangle::square(10);
    let rect2 = Rectangle::new(30, 40);
}

其中Self指代的是impl后的类名,因此我们之前用到的&self实际上是self : &Self的缩写。

一个结构体可以有多个impl块,并且多个impl块之间可以相互访问。

枚举类型

enum IpAddrKind {
    v4,
    v6,
}

struct IpAddr {
    kind: IpAddrKind,
    address: String,
}

fn main() {
    let home = IpAddr {
        kind: IpAddrKind::v4,
        address: String::from("127.0.0.1"),
    };
}

还有一中方法是我们可以把数据也存储到枚举类型当中

enum IpAddr {
    v4(u8, u8, u8, u8),
    v6(String),
}

fn main() {
    let home = IpAddr::v4(127, 0, 0, 1);
    let loopback = IpAddr::v6(String::from("::1"));
}

match 控制流

enum 也可以像 struct 一样使用impl定义成员函数。但是对于不同的类型,函数的实现肯定是不同的,这里就可以用mathc结构实现

enum IpAddr {
    v4(u8, u8, u8, u8),
    v6(String),
}
impl IpAddr {
    fn display(&self) {
        match self {
            Self::v4(a, b, c, d) => {
                println!("IPv4 : {}.{},{},{}", a, b, c, d)
            }
            Self::v6(s) => {
                println!("IPv6: {}", s)
            }
        }
    }
}
fn main() {
    let home = IpAddr::v4(127, 0, 0, 1);
    let loopback = IpAddr::v6(String::from("::1"));
    home.display();
    loopback.display();
}

match只会执行第一个匹配成功的。match不一定只能作用于枚举类型,也可是普通类型。并且如果是普通类型,如果枚举不完,match也提供了一个类似else的语法。

fn main() {
    let x = 1;
    match x {
        1 => {println!("one")},
        2 => {println!("two")},
        3 => {println!("three")},
        _ => {println!("other")},
    }
}

if letmatch的一个语法糖,下面两种写法是等价的

	let y = 2;

    match y {
        2 => {println!("yes")},
        _ => {println!("no")},
    }

    if let 2 = y{
        println!("yes");
    } else {
        println!("no");
    }

这样看if let似乎没有优势,但是对于下面这种情况

	let y = 3;

    match y {
        2 => {println!("yes")},
        _ => {},
    }

    if let 2 = y{
        println!("yes");
    } 

let ifelse 可以省略,但match_却不能省略。

Crate

Crate是rust中代码编译的最小单位。可以分为以下两类

  1. 二进制项,可以被编译为可执行程序,必须包含一个main函数作为程序的入口
  2. 库,没有main函数,也不能被编译为可执行程序。

包是提供一些列功能的一个或多个crate。一个包至少包含了一个Cargo.toml文件。一个包中至多包含一个库,可以任意个二进制项,但至少包含一个crate。

crate root 是一个源文件,编译器非以它为起点构建一个crate 的根模块。默认src/main.rs是与包名相同的二进制crate的根,src/lib.rs是与包名相同的库crate根。

模块

声明模块,在crate根文件中,用mod garden声明一个模块garden,编译器会按照如下顺序寻找模块的代码:

  • 内联,在大括号中,当mod garden后是大括号,不是分号。
  • 在文件src/garden.rs
  • 在文件gerden/mod.rs

声明子模块,在garden.rs中定义mod vegetables,编译器会按照如下顺序寻找模块代码

  • 内联
  • 在文件src/garden/vegetables.rs
  • 在文件src/garden/vegetables/mod.js

模块路径,当模块是crate的一部分是,可以在隐私规则运行的情况下,在crate的任何一个地方访问,比如vegetables下的Asparagus可以用crate::garden::vegetables::Asparagus访问。

一个模块内的代码默认对父模块私有,如果希望公用可以使用pub mod声明。

use关键字,对于上述的例子,可以用use crate::garden::vegetables::Asparagus;创建一个快捷链接方式,在之后的代码中用Asparagus即可访问。

src/main.rs

pub mod garden;

fn main() {
    let plant = crate::garden::vegetables::Asparagus {};
    println!("{plant:?}");
}

src/garden/mod.rs

pub mod vegetables;

src/garden/vegetables/mod.rs

#[derive(Debug)]

pub struct Asparagus {}

我们可以用cargo --lib testlib创建一个testlib

我们在src/lib.rs可以写

mod A {
    pub mod B {
        pub fn f() {
            println!("ok");
        }
    }
}

pub fn fuc() {
    A::B::f();
}

为什么在A不需要pub modfuc也可以正常调用?因为fucA在同一模块下,也就是兄弟模块,隐藏只是对父模块。

mod A {
    pub mod B {
        pub fn f() {
            println!("f ok");
            self::g();
        }

        pub fn g() {
            println!("g ok");
        }
    }
}

在同一个模块下相互调用不用pub,并且可以用self代指当前模块,其中self可以省略。

mod A {
    fn g() {
        println!("g ok");
    }
    pub mod B {
        pub fn f() {
            println!("f ok");
            super::g();
        }
    }
}

父模块也不对子模块私有,可以用super::代指父模块,类似Linux路径中的..

公有结构体

mod A {
    pub struct s {
        pub x: i32,
        y: i32,
    }
    impl s {
        pub fn new(x: i32, y: i32) -> s {
            s { x, y }
        }
        fn getX(&self) -> i32 {
            self.x
        }
        pub fn getY(&self) -> i32 {
            self.y
        }
    }
}

要注意的是虽然spub的,但是s的成员变量、成员函数默认都不是,需要显示的pub

公有枚举

mod A {
    pub enum B{
        x,
        y,
        z,
    }
}

对于公有枚举类型来说,枚举成员默认都是公有的。

posted @ 2025-05-05 17:43  PHarr  阅读(45)  评论(0)    收藏  举报