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
死循环。跳出去可以用 break
。continue
也是类似 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)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!