Rust 基本语法:变量、函数、所有权
Rust 是一门注重安全与性能的现代系统编程语言。它的语法简洁独特,尤其"所有权"机制是其核心特性。本文将快速掌握 Rust 的三大基础概念。
1. 变量与不可变性
1.1 变量声明
使用 let 关键字声明变量。默认情况下,Rust 变量是不可变的——这正是 Rust 安全性的基石。
fn main() {
let x = 5;
println!("The value of x is: {}", x);
}
尝试修改 x 的值会导致编译错误:
fn main() {
let x = 5;
x = 6; // 错误:无法修改不可变变量
}
1.2 可变变量
若需要修改变量值,使用 mut 关键字。
fn main() {
let mut x = 5;
println!("Before: {}", x);
x = 6;
println!("After: {}", x);
}
输出结果为 Before: 5 和 After: 6。
1.3 变量隐藏
可以在同一个作用域内重新声明同名变量,这称为"隐藏"(Shadowing)。
fn main() {
let x = 5;
let x = x + 1; // 隐藏:创建新变量 x
let x = x * 2; // 再次隐藏
println!("The value of x is: {}", x); // 输出 12
}
隐藏与 mut 的区别:隐藏可以改变类型,而 mut 不能。
fn main() {
let spaces = " "; // 字符串类型
let spaces = spaces.len(); // 转换为数字类型
println!("{}", spaces); // 输出 3
}
1.4 常量
常量使用 const 声明,必须标注类型,且值不可改变。
const MAX_POINTS: u32 = 100_000;
2. 函数
2.1 函数定义
使用 fn 关键字定义函数。函数名采用 snake_case 风格(小写下划线分隔)。
fn main() {
println!("Hello, world!");
another_function();
}
fn another_function() {
println!("Another function.");
}
2.2 函数参数
函数参数必须声明类型。
fn main() {
print_labeled_measurement(5, 'h');
}
fn print_labeled_measurement(value: i32, unit_label: char) {
println!("The measurement is: {}{}", value, unit_label);
}
调用 print_labeled_measurement(5, 'h') 输出 The measurement is: 5h。
2.3 函数返回值
Rust 函数返回值类型用 -> 符号声明。最后一个表达式的值即为返回值(无需 return 关键字)。
fn five() -> i32 {
5
}
fn main() {
let x = five();
println!("The value of x is: {}", x); // 输出 5
}
使用 return 可提前返回:
fn main() {
let result = plus_one(5);
println!("Result: {}", result); // 输出 6
}
fn plus_one(x: i32) -> i32 {
return x + 1;
}
2.4 语句与表达式
Rust 代码由语句和表达式组成。语句执行操作但不返回值,表达式计算并产生值。
fn main() {
let y = { // 语句块
let x = 3;
x + 1 // 表达式,无分号
}; // 语句结束需分号
println!("The value of y is: {}", y); // 输出 4
}
注意:表达式结尾无分号,分号使其变为语句(返回值单元类型 ())。
3. 所有权
所有权是 Rust 最独特的特性,它使 Rust 无需垃圾回收器即可保证内存安全。
3.1 所有权规则
Rust 所有权遵循三条核心规则:
- Rust 中的每个值都有一个变量作为其所有者。
- 同一时间只能有一个所有者。
- 当所有者离开作用域时,值会被丢弃。
fn main() {
let s1 = String::from("hello");
let s2 = s1; // s1 的所有权移动到 s2
// println!("{}, world!", s1); // 错误:s1 不再有效
println!("{}, world!", s2); // 正确:s2 拥有值
}
以上代码中,s1 的所有权转移给 s2,此后 s1 失效。这称为"移动"(Move)。
3.2 克隆与 Copy
若需要深拷贝,使用 clone() 方法(仅支持实现 Clone trait 的类型)。
fn main() {
let s1 = String::from("hello");
let s2 = s1.clone(); // 深拷贝
println!("s1 = {}, s2 = {}", s1, s2); // 两者都有效
}
对于标量类型(如整数、布尔、字符等),它们实现了 Copy trait,赋值时会自动拷贝。
fn main() {
let x = 5;
let y = x; // x 和 y 都有效
println!("x = {}, y = {}", x, y);
}
以下类型可自动 Copy:所有整数类型、布尔类型、浮点类型、字符类型、元组(仅当元素都 Copy 时)。
3.3 函数中的所有权
将值传递给函数时,所有权同样会转移。
fn main() {
let s = String::from("hello");
takes_ownership(s); // s 的所有权转移
// println!("{}", s); // 错误:s 已失效
let x = 5;
makes_copy(x); // x 仍有效(i32 是 Copy)
println!("x = {}", x);
}
fn takes_ownership(some_string: String) {
println!("{}", some_string); // some_string 离开作用域后被丢弃
}
fn makes_copy(some_integer: i32) {
println!("{}", some_integer); // some_integer 离开作用域后无影响
}
3.4 引用与借用
若不想转移所有权,可使用"引用"(Reference)。借用(Borrowing)指通过引用使用值而不获得所有权。
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1); // 借用 s1
println!("The length of '{}' is {}.", s1, len);
}
fn calculate_length(s: &String) -> usize { // s 是引用
s.len()
} // s 离开作用域,但不丢弃值
3.5 可变引用
默认引用不可修改。可变引用需要 &mut 声明,但作用域内同一时间只能有一个可变引用。
fn main() {
let mut s = String::from("hello");
change(&mut s); // 可变引用
println!("{}", s); // 输出 "hello, world!"
}
fn change(some_string: &mut String) {
some_string.push_str(", world!");
}
以下代码存在编译错误(防止数据竞争):
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s; // 错误:同一作用域内只能有一个可变引用
3.6 切片
切片(Slice)允许引用集合中的连续元素序列,而不获取所有权。
字符串切片
fn main() {
let s = String::from("hello world");
let hello = &s[0..5]; // 指向 "hello"
let world = &s[6..11]; // 指向 "world"
println!("{}", hello); // 输出 hello
println!("{}", world); // 输出 world
}
语法糖:[0..5] 等同于 [..5],[6..11] 等同于 [6..],完整字符串是 [..]。
函数改进
使用切片作为参数,使函数更通用。
fn first_word(s: &str) -> &str { // 接受字符串切片
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[..i];
}
}
&s[..]
}
此函数可接受 String 或 &str 类型。

暂无评论,快来抢沙发吧!