Rust Learning Notes
# recommended
curl --proto '=https' --tlsv1.3 https://sh.rustup.rs -sSf | sh
rustc --version
cargo --version
# update rust
rustup update
# install vscode extension: rust-analyzer
# In Debian
sudo apt update
sudo apt install rustc cargo rust-src rustfmt
rustc --version
cargo --version
# install vscode extension: rust-analyzer
# In Fedora
sudo dnf update
sudo dnf install rust rustfmt cargo rust-src
# install vscode extension: rust-analyzer
# add reqwest with feathere block
cargo add reqwest -F blocking
fn main() {
println!("hello, world");
println!("hello, 你好");
}
rustc hello_world.rs
./hello_world
# create new project
cargo new hello_cargo
# cargo new hello_cargo --vcs=none
tree .
.
├── Cargo.toml
└── src
└── main.rs
# build & run
cargo run
# 检查代码,确保能够编译成功,但是不产生可执行文件
cargo check
# release
cargo build --release
# Updating crates.io index(Cargo.lock)
cargo update
Cargo主要两个profile
- dev profile:
cargo build
- release profile:
cargo build --release
自定义profile
[package]
name = "minigrep"
version = "0.1.0"
edition = "2021"
[dependencies]
[profile.dev]
opt-level = 0
[profile.release]
opt-level = 3
[package]
name = "hell_cargo"
version = "0.1.0"
edition = "2021"
[dependencies]
# 引入rand包
rand = "0.8.5"
use rand::Rng;
use std::cmp::Ordering;
use std::io; // prelude // trait
fn main() {
println!("Guess!");
// let foo=1; // immutable
// let bar =foo;// immutable
let secret_num = rand::thread_rng().gen_range(1..101); // [1, 101)
println!("secret_num={}", secret_num);
loop {
let mut num = String::new(); // mutable
io::stdin().read_line(&mut num).expect("cannot read line!");
// shadow
// let num:i32=num.trim().parse().expect("Please enter a integer");
// 解析错误,程序退出
// let num:u32=num.trim().parse().expect("Please enter a integer");
// 解析错误,程序不退出
let num: u32 = match num.trim().parse() {
Ok(x) => x,
Err(_) => continue,
};
println!("Your Num={}", num);
match num.cmp(&secret_num) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => {
println!("You win!");
break;
}
}
}
}
模块系统(从上往下)
- Package: 用于 build, test, share crate
- Crate: 用于产生library, binary
- Module: 配合
use
使用,控制代码的组织、作用域、私有路径 - Path: 为struct, function, module命名的方式
Crate类型:
- binary
- library
Crate Root
- 是源代码文件,表示Rust编译器从这里开始,组成你的Crate的根Module
Package包含
- Cargo.toml, 描述如何构建这些Crates
- 包含0个或者1个 library crate
- 包含任意数量的binary crate
- 但是必须至少包含一个crate(binary or library)
$ cargo new project1
.
├── Cargo.toml
├── src
│ └── main.rs # 默认是binary crate的crate root,crate名于package名相同
如果有library
.
├── Cargo.toml
└── src
├── main.rs
└── lib.rs # package包含一个libray crate, 作为library crate的crate root, crate名与package名相同
Cargo会把crate root文件交给rustc来build library or binary
一个Package可以有多个binary crate:
- 文件放在src/bin
- 每个文件都是单独的binary crate
Module:
- 在一个Crate内,将代码进行分组
- 控制项目的public, private
- 建立modulde使用
mod
- 可以包含其他条目,比如sruct, enum, trait, function...,默认都是
private
- 父级模块无法访问子模块的private
- 子模块可以使用所有祖先模块的所有条目
// src/lib.rs
mod front_of_house {
mod hosting {
fn add_to_waitlist() {}
fn seat_at_table() {}
}
mod serving {
fn take_order() {}
fn serve_order() {}
fn take_payment() {}
}
}
src/main.rs和src/lib.rs都叫做crate roots, 这两个文件的内容形成了名为crate的模块,违约整个模块树的根部
crate # crate root
└── front_of_house
└── hostig
│ ├── buildadd_to_waitlist
│ └── seat_at_table
└── serving
├── take_order
├── serve_order
└──take_payment
- 绝对路径: 从crate root开始,使用crate名或者字面值
crate
- 相对路径:从当前路劲开始,self, super(上一级)或者当前模块的标识符
- 标识符以
::
分割
// src/lib.rs
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
fn seat_at_table() {}
}
mod serving {
fn take_order() {}
fn serve_order() {}
fn take_payment() {}
}
}
pub fn eat_at_restaurant() {
// 绝对路径调用, recommended
// 虽然front_of_house没有使用pub修饰,但是因为都是同一个文件根级
crate::front_of_house::hosting::add_to_waitlist();
// crate::front_of_house::serving::serve_order(); // error, serving is private
// 相对路径调用
front_of_house::hosting::seat_at_table();
}
// src/lib.rs
fn serve_order() {}
mod back_of_house {
fn fix_incorrect_order() {
cook_order();
// 相对调用
super::serve_order();
// 绝对调用
crate::serve_order();
}
fn cook_order() {}
}
struct:
pub struct
将struct设置成public- struct里面字段默认是private, 除了
pub struct
还得单独设置里面字段的可见性
// src/lib.rs
mod back_of_house {
pub struct Breakfast {
pub toast: String,
fruit: String, // private
}
}
// src/lib.rs
mod back_of_house {
pub enum Appetizer {
// enum前面添加pub,里面的字段都是pub
Sourp,
Salad,
}
pub struct Breakfast {
pub toast: String,
fruit: String, // private
}
impl Breakfast {
pub fn summer(toast:&str) ->Breakfast{
Breakfast { toast: String::from(toast), fruit: String::from("apple") },
}
}
}
pub fn eat_at_restaurant() {
let mut meal=back_of_house::Breakfast::summer("Rye");
meal.toast=String::from("Wheat");
// meal.fruit=String::from("Peach");// error, private
}
- 引入函数的时候,use指定到函数的父级
// src/lib.rs
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
fn seat_at_table() {}
}
mod serving {
fn take_order() {}
fn serve_order() {}
fn take_payment() {}
}
}
// use AbsolutePath
use crate::front_of_house::hosting;
// 相当于 mod hosting{}
// // use RelativePath
// // 因为front_of_house是同一级,所以直接去掉crate就行
// use front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist(); // 引入函数的时候,use指定到函数的父级,比如hosting
// hosting::seat_at_table(); // error, private
}
- 引入struct, enum的时候直接指定到struct, enum
// src/main.rs
use std::collections::HashMap;
fn main() {
let mut map=HashMap::new();
map.insert(1, 12.5);
}
- 同名的条目指定到父级
use std::fmt;
use std::io;
fn f1() -> fmt::Result {}
fn f2() -> io::Result {}
fn main() {}
- 使用
as
避免同名条目
use std::fmt::Result;
use std::io::Result as IoResult;
fn f1() -> Result {}
fn f2() -> IoResult {}
fn main() {}
use
的模块默认是private,想要外部也能访问,使用pub use
pub use crate::front_of_house::hosting;
如果vscode的rust-analyzer
一直卡住,先F1 Stop
,然后cargo build
如果获取https://crates.io/速度太慢,编辑 ~/.cargo/config
文件,添加以下内容:
[source.crates-io]
replace-with = 'tuna'
[source.tuna]
registry = "https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git"
标准库(std)也被当作是外部包
- 不需要修改Cargo.toml来包含std
- 需要使用特定条目的,才使用
use std::
use rand::Rng;
use std::cmp::Ordering;
use std::io;
fn main() {
let secret_num = rand::thread_rng().gen_range(1..101); // [1, 101)
println!("secret_num={}", secret_num);
}
- 简化同一个父级下的子条目
use rand::Rng;
use std::{cmp::Ordering, io};
fn main() {
let secret_num = rand::thread_rng().gen_range(1..101); // [1, 101)
println!("secret_num={}", secret_num);
}
` 引入父级和子级
// use std::io;
// use std::io::Write;
// 相当于上面的情况
use std::io{self, Write};
- 引入某个条目下面所有的使用通配符
use std::collections::*;
- 使用场景:将所有的被测试代码引入到tests模块
将模块拆分到多个文件
src
├── front_of_house.rs
├── lib.rs
└── main.rs
// main.rs
fn main() {}
// lib.rs
mod front_of_house; // ;意味着,将从front_of_house.rs里面找
use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
// front_of_house.rs
pub mod hosting {
pub fn add_to_waitlist() {}
fn seat_at_table() {}
}
mod serving {
fn take_order() {}
fn serve_order() {}
fn take_payment() {}
}
进一步拆分: 在模块后面加;
,然后 将{}
里面的内容移动到文件里面
src/
├── front_of_house
│ ├── hosting.rs
│ └── serving.rs
├── front_of_house.rs
├── lib.rs # 未改变
└── main.rs # 未改变
// front_of_house.rs
pub mod hosting;
mod serving;
// serving.rs
fn take_order() {}
fn serve_order() {}
fn take_payment() {}
// hosting.rs
pub fn add_to_waitlist() {}
fn seat_at_table() {}
// main.rs
use crate_name::mod1::mod2::mod3::Primary
use crate_name::mod1::mod2::mod3::Secondary
程序员写的代码多个层级,比较清晰;但是使用者难以找到某个类型;每次使用都得进入好几层
需要通过
pub use
暴露内部的api, 方便直接调用
// lib.rs
pub use self::mod1::mod2::mod3::Primary
pub use self::mod1::mod2::mod3::Secondary
// main.rs
use crate_name::Primary
use crate_name::Secondary
如果一个项目有多个library和一个binary(含有main.rs的project);它们彼此还有依赖,使用的时候每次都得独立编译,很麻烦;这个时候就采用workspace
# workspace Cargo.toml
[workspace]
members=[
"adder",
"add-one",
"add-two",
]
mkdir workspace1
# create workspace Cargo.toml
cargo new adder
cargo new add-one --lib
cargo new add-two --lib
workspace1/
├── adder
│ ├── Cargo.toml
│ └── src
│ └── main.rs
├── add-one
│ ├── Cargo.toml
│ └── src
│ └── lib.rs
├── add-two
│ ├── Cargo.toml
│ └── src
│ └── lib.rs
└── Cargo.toml
# adder/Cargo.toml
[package]
name = "adder"
version = "0.1.0"
edition = "2021"
[dependencies]
add-one={path="../add-one"}
add-two={path="../add-two"}
// adder/src/main.rs
use add_one; // cargo将add-one变成了add_one
fn main() {
let num=10;
println!("result={}", add_one::add_one(num));
}
// add-one/src/lib.rs
pub fn add_one(x: i32) -> i32 {
x + 1
}
cargo build
cargo run -p adder # 单独运行adder
cargo run # 也可以这么运行
cargo test # 测试所有
cargo test -p add-one #只测试add-one