起步
包管理(类比 JS 的 NPM)
Cargo config 文件样例:
[source.crates-io]
registry = "https://github.com/rust-lang/crates.io-index"
replace-with = 'ustc'
[source.ustc]
registry = "git://mirrors.ustc.edu.cn/crates.io-index"
语言基础
常量&变量
变量隐藏实际上只是复用了变量名,变量本身是完全重新申明的! 常量不可以被隐藏!
fn main() {
// 定义常量
const PI: f64 = 3.14;
// 定义可变变量
let mut num = 5;
// 输出 PI=3.14,num=5
println!("PI={},num={}", PI, num);
num = 10;
// 输出 num=10
println!("num={}", num);
// 定义不可变变量
let num2 = 5;
// 会报错:cannot assign twice to immutable variable
// num2 = 6; // 注释掉才能编译通过
println!("num2={}", num2);
// 变量隐藏,通过 let 关键词复用变量名
let num2 = 10;
// 输出 num2=10
println!("num2={}", num2);
// 隐藏可变变量 num,之后 num 将不可变了
let num = 20;
// num = 222; // 需要注释掉才能编译通过,证明是一个全新的不可变变量
// 输出 num=20
println!("num={}", num);
// 常量不可以被隐藏
// let PI = 11; // 需要注释掉才能编译通过
}
数据类型
fn main() {
// f64
let n1 = 4.63;
let n2: f32 = 4.63;
let c1: char = '中';
let b1 = n1 == n2;
println!("{}, {}", b1, c1);
// 定义字符串
let s1: &str = "中文";
println!("{} -> {}", c1, s1);
// 不加 r 前缀,转义
let s2: &str = "A\nB";
// 加上 r 前缀,原样输出
let s3: &str = r"A\nB";
println!("{} -> {}", s2, s3);
}
函数
fn main() {
fn1();
fn2(45);
let res3 = fn3(3);
println!("res3={}", res3);
}
// 无参数无返回值
fn fn1() {
println!("call fn1");
}
// 有参数无返回值
fn fn2(num: i32) {
println!("call fn2, arg num={}", num);
}
// 有参数有返回值
fn fn3(num: i32) -> i64 {
println!("call fn3");
return (num * 2).into();
}
控制流(循环&条件判定)
fn main() {
// Loop 循环
let mut i = 0;
loop {
i = i + 1;
println!("loop, i={}", i);
if i > 5 {
break;
}
}
// While 循环
while i < 15 {
i = i + 1;
println!("while, i={}", i);
}
// For 循环
for j in (i..20) {
println!("for, j={}", j);
}
// For 循环,包含 25
for j in (i..=25) {
println!("for, j={}", j);
}
// IF 判定
println!("i={}", i);
if i == 15 {
println!("i={}", i);
}
// 条件表达式作为结果
let res = if i == 15 { 5 } else { 6 };
println!("res={}", res);
}
所有权
Rust 中非常特别设计,通过所用权的引用和借用,实现了无GC下的内存管理。 这个在我看来是 Rust 的设计理念之一,需要重点理解!
fn main() {
let s1 = "s1";
let s1_copy = s1;
println!("s1={}, s1_copy={}", s1, s1_copy);
let s2 = String::from("s2");
// 变量复制 -> 指针移动,原始指针会失效
let s2_copy = s2;
// 如下语句会失效,需要注释才能运行
// println!("s2={}", s2); // error: value borrowed here after move
// 手动指定要进行深拷贝
let s2_new = s2_copy.clone();
println!("s2_copy={}, s2_new={}", s2_copy, s2_new);
// 引用移动
let s3 = String::from("Hello");
let s3_len = calculate_string_length(s3);
// println!("s3={}, s3 len={}", s3, s3_len); // 会报错, s3 已经被释放
// 借用(用完会把s4的所有权换回来)
let s4 = String::from("Rust");
let s4_len = calculate_string_length_pass_address(&s4);
// 此时可以正常打印出来
println!("s4={}, s4 len={}", s4, s4_len);
}
fn calculate_string_length(str: String) -> usize {
str.len()
}
fn calculate_string_length_pass_address(str: &String) -> usize {
return str.len();
}
所有权(悬垂指针)
// 特性:所有权(悬垂引用:悬垂指针是其指向的内存可能已经被分配给其它持有者)
fn main() {
// 如下调用会报错,返回的引用指向的内存区域已经被释放了
// let reference_to_nothing = dangle();
let safe_str = safe();
println!("safe_str={}", safe_str);
}
// 需要先注释,避免编译错误
// fn dangle() -> &String {
// let s = String::from("hello");
// &s // 返回字符串 s 的引用,s 离开函数就被释放,引用是无效的
// }
fn safe() -> String {
let s = String::from("hello");
s
}
Slice 类型
// Slice 类型(无所有权的类型,字符串字面量就是slice)
fn main() {
let str = String::from("hello rust");
let part1 = &str[0..5];
let part2 = &str[6..str.len()];
println!("str={}, part1={}, part2={}", str, part1, part2);
// 获取全部 str
let str_copy = &str[..];
println!("str={}, str_copy={}", str, str_copy);
}
猜数字游戏
use rand::Rng;
use std::cmp::Ordering;
use std::io;
fn main() {
let source = rand::thread_rng().gen_range(1..=100);
println!("猜数字游戏!随机数字[0-100]已就绪");
// 循环进行猜测~
loop {
println!("请输入你的猜测:");
// 定义一个可变变量
let mut num = String::new();
io::stdin().read_line(&mut num).expect("输入错误");
// 转换输入的字符串为数字
// trim 是为了移除输入内容中的换行符
let num: u16 = match num.trim().parse() {
Ok(num) => num,
Err(_) => {
println!("你的输入是{},不是数字,请重新输入", num.trim());
// 重新输入
continue;
}
};
// 比较两个数字
match num.cmp(&source) {
Ordering::Equal => {
println!("恭喜,猜测正确!");
// 退出循环
break;
}
Ordering::Greater => {
println!("你猜得太大了!");
}
Ordering::Less => println!("你猜得太小了!"),
}
}
}
常见问题
- VSCode 中安装 Rust 插件后,无法启动 Language Server
这是由于这个插件默认的 rustup 地址不对,需要在 Code -> Perferences -> Settings 中去修改“rust-client.rustupPath”的值为具体的 rustup 地址。该地址一般可用如下方式找到:
# 找到 cargo bin 目录
cd ~/.cargo/bin
# 打印当前目录路径
pwd
# 拼接上 rustup 进行配置,可参考下图