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 项目,
1 | $ cargo new rust-proj |
Cargo 会在 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!
1 | fn main() { |
println!调用了一个 Rust 宏(macro)。如果是调用函数,则应输入println(没有!)。我们将在第十九章详细讨论宏。现在你只需记住,当看到符号!的时候,就意味着调用的是宏而不是普通函数,并且宏并不总是遵循与函数相同的规则。
cargo new 生成的项目中,包含配置文件 Cargo.toml
文件名: Cargo.toml
1 | [dependencies] |
其中"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 可以直接将数据附加到枚举的每个成员上,这样就不需要一个额外的结构体了。
1 | enum IpAddr { |
Rust 没有空值(Null),因为空值会导致很多错误,而是实现了一个枚举变量
Option<T>:
1 | enum Option<T> { |
关于字符串方法,以下两种方法等效:
1 | let s1 = String::from("initial contents"); |
参考链接: