Rust 笔记

https://github.com/ACMClassCourse-2022/Summer-Ray-Tracer

Rust 这门语言真的是挺难的,主要在于编译器贼事儿逼,什么都要管。这篇文章主要内容是给 C++ 的每一样东西一个 Rust 平替。

以下内容主要参考自 Rust Programming Language 中文版

I/O

输出

print!(), println!()。其中的感叹号代表宏。用法:

let a = 3;
println!("a = {a}");
println!("a = {}", a);
println!("a = {b}", b = a);

用法跟 scanf 比较像,但是比 scanf 方便多了。转义同样用 \

输入

需要 use std::io。这句话的功能更偏向 include 而不是 using,因为你不 use 根本用不了。

io::stdin()
    .read_line(&mut guess)
    .expect("Failed to read line");

read_line 表示读一行。guess 一般要求是 mut String,后面可以利用屏蔽性,让一个同名数值变量屏蔽它。

expect 可以捕捉错误。read_line 是有返回值的,返回类型是一个内置类型,叫做 Result。它有两种可能值:Ok(_)Err(_)_ 表示通配符)。expect 会处理 Err 下的情况。如果返回 Err,程序会 panic,直接退出。

看一个用 match 结构(类似 switch-case)捕捉错误的例子(后面也会用到):

let guess: u32 = match guess.trim().parse() {
    Ok(num) => num,
    Err(_) => continue,
};

分支结构:

if

if 不止可以控制语句的执行,还可以表达一个有多种可能的表达式。

先看看表达式的复杂表示:

let y = {
    let x = 3;
    x + 1
};

if 用法:

if number % 4 == 0 {
    println!("number is divisible by 4");
} else if number % 3 == 0 {
    println!("number is divisible by 3");
} else if number % 2 == 0 {
    println!("number is divisible by 2");
} else {
    println!("number is not divisible by 4, 3, or 2");
} // 没有小括号,其他跟 C++ 一样

let mut a = if b == 0 {1} else {0};

注意 if 里的 condition 一定要是一个 bool 类型。如果 a 是一个 i32,就不能直接 if a {...}。这一点跟 C/C++ 不一样。

match

用法见上。控制语句执行和表达式都可以。每个 case 的值跟控制的语句之间写 =>。每个 case 没有大括号的话后面跟逗号,有大括号可以加逗号可以不加。

高级用法:占坑。

if let

看不懂。到时候再说。

循环结构

loop

死循环。跳出去可以用 breakcontinue 也是类似 C++ 的。不一样的地方在于有一个东西叫做循环标签。C++ 想直接退出两层及以上循环必须在外面的循环开 flag 变量,但是 Rust 可以开循环标签,直接在里面退出外层循环。

'outer: loop {
    println!("Entered the outer loop");
    'inner: loop {
        println!("Entered the inner loop");
        // 这只是中断内部的循环
        //break;
        // 这会中断外层循环
        break 'outer;
    }
    println!("This point will never be reached");
}

while

条件不用加括号,别的没什么。

for

左闭右开:for n in 0..10;闭区间:for n in 1..=10

变量与常量

整数:i8 u8 i16 u16 i32 u32 i64 u64 isize usize

浮点数:f32 f64

char:四个字节,Unicode。

bool:略。

():称为 Unit,可以看作一个空 tuple。

一般的变量不能改变,如果要改变在变量前加上 mut

变量定义时不需要规定类型,整数默认 i32,浮点数默认 f64

数组和元组。可不可以改变需要统一。0-based 访问,数组用中括号,元组用.0 .1 etc. 看清楚逗号和分号:数组中 [2;3] 表示 [2, 2, 2],而 [2,3] 就是 [2, 3]。数组必须在编译时确定长度,如果不能,可以用 Vector。数组类型的用 [varType; elementNum] 表示。

切片:区间表示法。一般表示为 &[T]。特别地,String 的切片表示为 &str。一般的常量字符串类型是 &str,可以用 String 的成员函数 from 来将一个 &str 转化为 String

常量:const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;

可以是表达式,但必须在编译时确定。必须规定类型。

所有权

把特定数据存到堆里面:Box::new(Point { x: 0.0, y: 0.0 }) 返回值是一个指针。

String 也存在堆里。

存在堆里的数据:浅拷贝用等号,拷贝后以前的指针作废,不能指向相应的值。这种情况下所有权移交给后面的变量。深拷贝用 clone

let s1 = String::from("hello");
let s2 = s1.clone();

这样就是拷贝值了。

函数传参时,值传递会把所有权移交给函数体,然后 就不能用了!无论存在栈里还是堆里都一样。

如果还想复用,可以传引用进去。传参时用 &var_name,函数头写 s: &String。这种引用不能改变值,相当于 C/C++ 里面的 const &

如果想动值,把函数头改成 s: &mut String 即可。

函数

怎么把它给忘了……

无返回值的函数:

fn another_function(x: i32) {
    println!("The value of x is: {x}");
}

有返回值的函数:最后一行是一个表达式,表示返回值。

fn plus_one(x: i32) -> i32 {
    x + 1
}

结构体

终于快写到面向对象了。

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

fn main() {
    let mut user1 = User {
        active: true,
        username: String::from("someusername123"),
        email: String::from("someone@example.com"),
        sign_in_count: 1,
    };

    user1.email = String::from("anotheremail@example.com");
}

注意在构造的时候必须规定每一个初值。mut 必须作用于整个结构体,不能单独让某几个变量成为 mut

一个比较有用的简写:以下两份代码等价。

let user2 = User {
    active: user1.active,
    username: user1.username,
    email: String::from("another@example.com"),
    sign_in_count: user1.sign_in_count,
};
// -----------------
let user2 = User {
    email: String::from("another@example.com"),
   ..user1 // .. 表示剩余
};

元组结构体:

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

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

面向对象,启动!

Method

定义于 Rectangle 结构体上的 area 方法:

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

impl Rectangle {
    fn area(&self) -> u32 { // 成员函数:可以选择参数类型
	// &self 是 self: &Self 的简写,Self 表示这个类的名字
        self.width * self.height
    }
}

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

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

当使用 object.something() 调用方法时,Rust 会自动为 object 添加 &、&mut 或 * 以便使 object 与方法签名匹配。也就是说,这些代码是等价的:

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

终于见到 Rust 难得的一种简化。

Rc:多重所有权

Rc::new() 创建新的对象,传一个类的构造函数调用,返回一个指针。

Rc::clone() 接受一个已有对象的引用,如 Rc::clone(&a),创建指向它的一个指针。

这样的指针是安全且智能的:它会检测最后一次使用这些元素的时间,在此后它会自动消亡,释放堆空间。

注意 Rc::clone()a.clone() 的区别:后者是深拷贝,前者是浅拷贝,增加引用计数。

得到引用计数:Rc::strong_count(&a)

posted @   gznpp  阅读(36)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示