Rust 是一门偏底层的,安全的,高效的开源编程语言。
本文记录了作者学习 Rust 语言的笔记和心得,涵盖从工具链到语言特性的核心内容。首先介绍了 rustup、cargo 等基础工具的安装和使用方法;然后详细讲解了 Rust 的变量特性、数据类型和内存管理模型,包括不可变变量、栈与堆的区别、所有权系统等独特概念;最后总结了 Rust 借鉴其他语言的优点以及自身的创新特性。这篇文章为 Rust 初学者提供了简明扼要的入门指南和关键知识点概览。
工具链#
rustup 是管理 Rust 版本和相关工具的命令行工具 (rust toolchain installer)
-
rustup update更新Rust版本 -
rustup self uninstall卸载Rust以及rustup本身
Rust 工具链中包括 rustc 编译器工具,rustfmt 格式化工具,rustdoc 文档化工具等。
rustc 类似于 C/C++ 中的 gcc/clang
Cargo 是 Rust 的构建系统和包管理器。可以进行构建代码、下载依赖库并编译这些库等。
cargo new rust-proj 创建一个 Rust 项目,
$ cargo new rust-proj Created binary (application) `rust-proj` package$ tree rust-projrust-proj├── Cargo.toml└── src └── main.rs
1 directory, 2 filesCargo 会在 hello_cargo 目录初始化一个 git 仓库,以及一个 .gitignore 文件。如果你在现有的 git 仓库中运行 cargo new,则不会生成 git 文件;你可以通过使用 cargo new --vcs=git 来覆盖此行为。
可以通过 --vcs 参数使 cargo new 切换到其它版本控制系统(VCS),或者不使用 VCS。运行 cargo new --help 参看可用的选项。
cargo build 构建项目,目标可执行文件在 target/debug/ 下
cargo run 则是构建项目并运行。
cargo check 检查项目代码正确,确保可编译。不会产生可执行文件。
cargo build 默认构建是 debug 模式。如果项目一切都 OK,可以运行 cargo build --release 执行发布构建,这会对项目进行一定编译优化,从而使得代码运行得更快,但是相应地,编译时间会更长。
cargo doc --open 会生成当前项目中的依赖库 (crate) 的文档,并转到浏览器可以查询。
Hello, World!#
fn main() { println!("Hello, world!");}println!调用了一个 Rust 宏(macro)。如果是调用函数,则应输入println(没有!)。我们将在第十九章详细讨论宏。现在你只需记住,当看到符号!的时候,就意味着调用的是宏而不是普通函数,并且宏并不总是遵循与函数相同的规则。
cargo new 生成的项目中,包含配置文件 Cargo.toml
文件名:Cargo.toml
[dependencies]
rand = "0.8.3"其中”0.8.3”指定依赖版本,其语法遵循语义化版本 (Semantic Versioning)
这里的 0.8.3 实际上是 ^0.8.3 的简写,它表示 任何不低于 0.8.3,但是低于 0.9.0 的版本。Cargo 将这些版本视作与 0.8.3 版本公有 API 相兼容的版本,这个声明确保你将获得最新的补丁版本,它仍然可以与本章中的代码正常编译。0.9.0 或以上版本不保证拥有接下来示例中使用到的 API。
一些定义#
-
Rust 变量默认是不可改变的(immutable),如果想声明可变变量,需要使用
mut关键字。 -
Rust 不允许对常量使用
mut。常量不光默认不能变,它总是不能变。其声明使用const,且必须标注类型。 -
整型变量分有符号和无符号,如
i8表示有符号 8 位整数,u8表示无符号 8 位整数,i128表示有符号 128 位整数。 -
还有一种整型依赖计算机架构,
isize,usize在不同的架构中表示的长度会不同。 -
整型字面值有十六进制 (
0xff),八进制 (0o77),二进制 (0b11),以及十进制 (99_999),单字节字符 (仅限u8) -
Rust 默认数字类型是
i32 -
浮点型变量有两种,
f32表示单精度浮点小数,f64表示双精度浮点小数。默认是f64。 -
字符类型(char)用单引号表示,其大小为 4 个字节,表示了一个 Unicode 标量值。
-
复合类型元组 (tuple) 一旦声明,其长度不变,其中每一个位置都有一个类型的值,类型可以不同。
-
复合类型数组 (array) 长度也是固定的,而且其中类型必须相同。
语句 (statement) 和表达式 (expression)
Rust 是一门基于表达式的语言。
语句是执行一些操作,但是不返回值的指令。
函数定义 就是一个语句。
表达式计算并产生一个值,可以赋予其它变量。
函数调用 是一个表达式,大括号创建的块作用域也是一个表达式。
表达式的结尾没有分号,如果结尾加上分号,则变成了语句。
数字本身就是一个表达式,所以它可以返回本身的值赋值给其它变量。
栈 (stack) 中的数据必须占用已知且大小固定的大小。
当数据大小未知,或大小可能变化的时候,需要用堆 (heap)。比如使用内存分配器 memory allocator 分配指定大小的内存,此时该块内存会被标记为已用,并且以指针来表示这块内存。
而将数据放入栈中的过程并不叫做分配内存,因为这个过程并没有新的内存被分配。
堆的指针可以存储在栈上。
入栈比在堆上分配内存要快。
访问堆中的数据比访问栈上的数据要慢,因为访问堆首先要通过指针来访问,而指针在栈上。
一些笔记#
- 定义变量用动词 let
- 借鉴 Python 的元组
- 借鉴 C++ 的指针
- 完善的包管理工具 cargo
- 友好的文档管理
- 使用先进的 VCS 管理项目
- 无垃圾回收机制,使用变量所有权管理内存
- 编译与执行分开,更早的发现错误
- 浅拷贝与深拷贝,默认不进行数据的深拷贝,深拷贝使用 clone 方法
- 移动 (move) 操作
- Copy trait
- 栈上数据;堆上数据;既在栈上,又在推上 (数据指针在栈上,数据内容在堆上) 数据
- 默认行为:默认变量不可变
- 引用 (ref) 变量默认也不能修改其引用的值
- 一个引用的作用域从声明的地方开始一直持续到最后一次使用为止
- 字符串 slice
- Rust 可以直接将数据附加到枚举的每个成员上,这样就不需要一个额外的结构体了。
enum IpAddr { V4(String), V6(String),}let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));- Rust 没有空值 (Null),因为空值会导致很多错误,而是实现了一个枚举变量
Option<T>:
enum Option<T> { Some(T), None,}
let some_number = Some(5);let some_string = Some("a string");# 当为 `None` 时,必须显示指定类型 `<T>`let absent_number: Option<i32> = None;关于字符串方法,以下两种方法等效:
let s1 = String::from("initial contents");let s2 = "initial contents".to_string();参考链接: