Rust笔记(一)
开端
Windows上的Rust有两个版本MSVC和GNU,这里涉及到Microsoft C++ Build Tools(对应于MSVC版本,官方推荐)安装c++环境,或者MSYS2(对应于GNU版本)安装c++环境。
其它安装的细节,以及在其它系统上的安装方式,请仔细阅读以上参考文档。
可以使用命令rustup doc查看文档。
在neovim里配置Rust开发环境
- 安装
rust-analyzer,这是Rust的LSP,需要对"neovim/nvim-lspconfig"进行配置。 - 安装"saecki/crates.nvim"插件,这个插件用来提供依赖信息的增强支持。
以上两者的配置如下plugins\lsp.lua所示:
1 | return { |
- 配置Rust调试,参考nvim-dap
具体配置如plugin\dap-rust.lua所示,需要修改codelldb.exe的路径:
1 | return { |
调试小技巧技巧
这些快捷键与我的配置有关。
- 不熟悉快捷键时可以pin一个快捷键截图。
- 善用
<C+方向键调整窗口大小。 <leader>dK可以查看光标位置的变量值。<leader>dr可以打开REPL,能够输入表达式并查看结果。
Cargo
- cargo项目的主目录结构是有要求的
Cargo.toml、src/在项目顶层。- 源代码全都在
src/目录下。 - 使用
cargo new <name>命令创建新项目时,Cargo会自动创建Cargo.toml和src/main.rs文件。 - 也可以使用
cargo init <name>命令在当前目录初始化Cargo项目。
cargo build命令会编译当前目录下的Cargo项目。- 编译结果会放在
target/debug/目录下。 - 同时会生成
cargo.lock文件,记录依赖的版本信息。 - 如果需要编译成发布版本,编译器会对代码进行优化,编译时间更长,但执行更快,可以使用
cargo build --release,结果会放在target/release/目录下。j
- 编译结果会放在
cargo run命令会编译并运行当前目录下的Cargo项目。- 但是如果项目没有修改,Cargo会跳过编译步骤,直接运行上次编译的结果。
cargo check可以检查代码是否有错误,但不会生成可执行文件。- 它比
cargo build快很多,这对于快速检查代码很有用,尤其是在开发过程中。
- 它比
[dependencies],rand = "0.9.1"与rand = "^0.9.1"等价。^表示兼容版本,允许使用0.9.x版本。~表示兼容次版本,兼容范围可能与^一致或小一点。=表示精确版本,必须使用0.9.1。
cargo update命令会更新依赖到最新的兼容版本。- 它会修改
Cargo.lock文件,但不会修改Cargo.toml文件。
- 它会修改
发展
数据
变量与常量
变量分成可变变量和不可变变量,都需要使用let声明。
- 可变变量需要额外加上
mut。
常量使用const声明,必须指定类型。
不可变变量与常量的区别
- 不可变变量可以在运行时计算值,常量必须在编译时确定。
- 不可变变量的特性:遮蔽,重新声明以覆盖。
1 | let x = 5; // 不可变变量 |
- 常量的特性:全作用域可见:
- 常量不管在何处声明都会作用域整个作用域,因为编译器会将常量名替换成值。
- 同一个作用域内不能有同名的常量,原因如上,会冲突。
- 内层作用域可以遮蔽外层作用域的常量,因为编译器会先在当前作用域匹配,找不到才会去外层作用域找。
1
2
3
4
5
6
7
8
9
10
11const VALUE: u32 = 100_000;
fn main() {
println!("The value is: {}", VALUE);
const VALUE: u32 = 200_000;
{
const VALUE: u32 = 300_000;
println!("The value is: {}", VALUE);
}
println!("The value is: {}", VALUE);
}
下划线标记未使用的变量
未使用的变量往往意味着潜在的 bug ,可以使用开头的下划线告诉编译器允许变量未使用,例如:
1 | fn main() { |
这里_x被标记为未使用,编译器不会报错,而y会报错。
或者使用编译器属性(attribute)#[allow(unused_variables)]来关闭对未使用变量的警告。
变量解构
赋值语句左边可以使用元组、切片和结构体进行解构,例如:
1 | struct Struct { |
变量遮蔽
Rust 允许声明相同的变量名,在后面声明的变量会遮蔽掉前面声明的。
补充
宏(macro)
The suffix ! is used to mark something as a macro invocation, not a function call. A macro will be replaced by the compiler with the Rust code it generates.
For example:
1 | print!("Hello, world!"); |
This is a macro call, not a function call. The compiler expands it into an expression that performs text formatting, writes to standard output, and handles potential I/O errors.
Two major systems to define macros
- Declarative macros (defined using macro_rules!)
1 | macro_rules! say_hello { |
Common fragment specifiers (which define matchable code types):
| Specifier | Matches |
|---|---|
ident |
Identifiers (e.g., variable names) |
expr |
Expressions |
ty |
Types |
pat |
Patterns |
block |
Code blocks{ … } |
item |
Items like functions or structs |
path |
Paths (foo::bar) |
tt |
Token trees (raw tokens) |
meta |
Metadata inside attributes (#[…]) |
- Procedural macros
WARNING: I don’t understand it now
Procedural macros are more advanced and work at the token-stream level rather than pattern matching. They let you manipulate the abstract syntax tree (AST) of the Rust code directly.
They come in three forms:
| Type | Description | Example |
|---|---|---|
| Function-like macros | Called like functions with!syntax. | custom_macro!(...) |
| Derive macros | Used with#[derive(...)]for implementing traits automatically. |
#[derive(Debug)] |
| Attribute macros | Custom attributes applied to functions or modules. | #[route(GET, "/")] |




