《Rust编程之道》学习笔记二第2章语言精要好读书,不求甚解;每有会意,便欣然忘食。1Rust语言的基本构成Rust语言主要由以下几个核心部件组成:语言规范编译器核心库标准库包管理器语言规范Rust语言规范主要由Rust语言参考(TheRustRefe
好读书,不求甚解;每有会意,便欣然忘食。
Rust 语言规范主要由 Rust 语言参考(The Rust Reference)和 RFC 文档共同构成。
The Rust Reference:<https://doc.rust-lang.org/stable/reference/>
Rust 参考手册 中文版:<https://rustwiki.org/zh-CN/reference/>
Rust 规范文档:https://rustwiki.org/wiki/
Rust 官方文档中文教程:https://rustwiki.org/
Rust 是一门静态编译型语言。Rust官方的编译器叫 rustc,负责将 Rust 源代码编译为可执行文件或其他库文件(.a、.so、.lib、.dll 等)。
Rustc 有如下特点:
Rust 语言的语法由核心库和标准库共同提供。
Rust 核心库是标准库的基础。
可以通过在模块顶部引入 #![no_std]
来使用核心库。
核心库包含如下部分:
标准库包含的内容大概如下:
把按一定规则组织的多个 rs 文件编译后就得到一个包(crate)。
包是 Rust 代码的基本编译单元,也是程序员直接共享代码的基本单元。
Rust 社区的公开第三方包都集中在 crates.io 网站上面,它们的文档被自动发布到 docs.rs 网站上。
Rust 提供了非常方便的 包管理器 Cargo。
https://doc.rust-lang.org/cargo/
https://rustwiki.org/zh-CN/cargo/index.html
cargo new bin_crate
cargo new --lib lib_crate
cargo new 命令默认可以创建一个用于编写可执行二进制文件的项目。
cargo new 命令添加 --lib 参数,可以创建用于编写库的项目。
cargo build 对项目进行编译
cargo run 项目运行
Rust 中的语法可以分成两大类:语句(Statement)和表达式(Expression)。
语句是指要执行的一些操作和产生副作用的表达式。
表达式主要用于计算求值。
语句分为两种:声明语句(Declaration statement)和表达式语句(Expression statement)。
// extern crate std;
// use std::prelude::v1::*;
fn main() {
pub fn answer() -> () {
let a = 40;
let b = 2;
assert_eq!(sum(a, b), 42);
}
pub fn sum(a: i32, b: i32) -> i32 {
a + b
}
answer();
}
Rust 会为每个 crate 都自动引入标准库模块,除非使用 #![no_std]
或 #[no_std]
属性明确指定了不需要标准库。
关键字 fn 是 function 的缩写。
单元类型拥有唯一的值,就是它本身。
单元类型的概念来自 OCaml,它表示”没有什么特殊的价值“。
函数无返回值的函数默认不需要在函数签名中指定返回类型。
assert_eq! 是宏语句,是Rust提供的断言,允许判断给定的两个表达式求值结果是否相同。
Rust 编译器在解析代码的时候,如果碰到分号,就会继续往后面执行;
如果碰到语句,则执行语句;
如果碰到表达式,则会对表达式求值,如果分号后面什么都没有,就会补上单元值()。
当遇到函数的时候,会将函数体的花括号识别为块表达式(Block Expression)。
块表达式是由一对花括号和一系列表达式组成的,它总是返回块中最后一个表达式的值。
在某种程度上,可以将Rust看作是一切皆表达式。
let 关键字创建变量
let 创建的变量一般称为 绑定(Binding)
它表明了标识符(Identifier)和值(Value)之间建立的一种关联关系。
Rust 中的表达式一般可以分为位置表达式(Place Expression)和值表达式(Value Expression)。
在其他语言中,一般叫做左值(LValue)和右值(RValue)
位置表达式是表示内存位置的表达式。分别有以下几类:
通过位置表达式可以对某个数据单元的内存进行读写。
除此之外的表达式就是值表达式。
值表达式一般只引用了某个存储单元地址中的数据,它相当于数据值,只能进行读操作。
从语义角度来说,位置表达式代表了持久性数据,值表达式代表了临时数据。
表达式的求值过程在不同的上下文中会有不同的结果。
求值上下文也分为位置上下文(Place Context)和值上下文(Value Context)
位置上下文的表达式:
除了上述几种情况,其余表达式都属于值上下文。
值表达式不能出现在位置上下文中。
pub fn temp() -> i32 {
return 1;
}
fn main() {
let x = &temp();
temp() = *x; // error
}
temp 函数调用时一个无效的位置表达式,它是值表达式。
使用 let 关键字声明的位置表达式默认不可变,为不可变绑定。
fn main() {
let a = 1;
// a = 2; // immutable and error
let mut b = 2;
b = 3; // mutable
}
通过 mut 关键字,可以声明可变的位置表达式,即可变绑定。
可变绑定可以正常修改和赋值。
从语义上来说
let 默认声明的不可变绑定只能对相应的存储单元进行读取
let mut 声明的可变绑定可以对相应的存储单元进行写入
当位置表达式出现在值上下文中时,该位置表达式将会把内存地址转移给另外一个位置表达式,这其实是所有权的转移。
fn main() {
let place1 = "hello";
let place2 = "hello".to_string();
let other = place1;
println!("{:?}", place1);
let other = place2;
println!("{:?}", place2); // Err: other value used here after move
}
因为 place1 是一个位置表达式,现在出现在了赋值操作符右侧,即一个值上下文内,所以 place1 会将内存地址转移给 other。
在语义上,每个变量绑定实际上都拥有该存储单元的所有权,这种转移内存地址的行为就是所有权(OwnerShip)的转移,在Rust中称为移动(Move)语义,那种不转移的情况实际上是一种复制(Copy)语义。
Rust 没有GC,所以完全依靠所有权来进行内存管理。
Rust 提供借用(Borrow)操作符(&),可以直接获取表达式的存储单元地址,即内存位置。
可以通过该内存位置对存储进行读取。
fn main() {
let a = [1,2,3];
let b = &a;
println!("{:p}", b); // 0x7ffcbc067704
let mut c = vec![1,2,3];
let d = &mut c;
d.push(4);
println!("{:?}", d); // [1,2,3,4]
let e = &42;
assert_eq!(42, *e);
}
通过 println! 宏指定 {:p} 格式,可以打印 b 的指针地址,也就是内存地址。
注意,要获取可变引用,必须先声明可变绑定。
对于字面量 42 来说,其本身属于值表达式。
通过借用操作符,相当于值表达式在位置上下文中进行求值,所以编译器会为 &42 创建一个临时值。
值表达式在位置上下文中求值时会被创建临时值
将“ & ”称为借用操作符,通过借用操作符得到的类型叫引用(Reference)类型。
main 函数代表程序的入口。
函数是通过关键字 fn 定义的。
定义一个 FizzBuzz 函数:输入一个数字,当数字是3的倍数时,输出fizz;当数字是5的倍数时,输出buzz;
当数字是3和5共同的倍数时,输出 fizzbuzz;其他情况返回该数字。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!