rust
Rust基础 知识:
std::fmt:
- format! 的使用 方法: positional params:, named params: , formating params:
示例
1 2 3 4
format!("Hello, {}!", "world"); // => "Hello, world!" format!("{1} {} {0} {}", 1, 2); // => "2 1 1 2" format!("{argument}", argument = "test"); println!("Hello {:1$}!", "x", 5);
- formating trait: 实践显式 对于外部的Type {}确实需要impl Display, 而{:?} 需要 impl Debug
- {} => Display
- => Debug
- => Octal
- => Pointer
- fmt::Display vs fmt::Debug
- Display: 断言 实现者 总是返回 UTF-8 的字符串, 并非所有的 都实现了 Display * Debug: 应该为所有pub type 实现, 输出为 内部状态, 该Trait 的目的是为了方便Rust Debug, 可以 使用#[derive(Debug)] 来使用默认的内部实现
array & Slice
- array的类型为: [T: len], let mut array: [i32; 3] = [0; 3];
- Slice: [T]
structures: 1) Tuple struct, 2) classic struct 3) Unit structs
- struct Pair(i32, f32);
- struct Person { name: String, age: u8}
- struct Unit; unit Struct 没有任何的 field
Enums: 包含多个变体的组合项, 任何一个变体都是一个正确的 enum 类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
enum WebEvent {
// An `enum` may either be `unit-like`,
PageLoad,
PageUnload,
// like tuple structs,
KeyPress(char),
Paste(String),
// or c-like structures.
Click { x: i64, y: i64 },
}
// A function which takes a `WebEvent` enum as an argument and
// returns nothing.
fn inspect(event: WebEvent) {
match event {
WebEvent::PageLoad => println!("page loaded"),
WebEvent::PageUnload => println!("page unloaded"),
// Destructure `c` from inside the `enum`.
WebEvent::KeyPress(c) => println!("pressed '{}'.", c),
WebEvent::Paste(s) => println!("pasted \"{}\".", s),
// Destructure `Click` into `x` and `y`.
WebEvent::Click { x, y } => {
println!("clicked at x={}, y={}.", x, y);
},
}
}
Type Alias:
type 关键字能够 使用 type Name = ExistingType; 语法来 使用 Name 代替 ExistingType 使用。 Self 是一种Type Alias
const, static
Variable Bindings:
1)变量默认 是不可修改的, 使用mut 改变 2) 可以在内部的scope中 其同样的名字来shadow 外部的变量 3)可以使用 先声明 后设定数值的形式 使用变量,但是rust 会检查 使用 未定义变量的错误, 来预防因此产生的问题
Types:
1)转换 as关键字 2) type alias: type NanoSecond = u64; 3) 数值的类型,可以添加到 后面最为后缀使用, 例如: 42i32
Conversion: rust 的struct 以及 enum 等自定义类型的 type转换
From & Into:
- From 为一个类型定义, 如何create self 从 另一个type中转变
- 则是From 的 调用者, From
for U 自动实现了 Into\<U\> for T, blank implement
TryFrom & TryInto:
类似于 From & Into 不同的是, 转换可能失败,返回Result
ToString & FromStr:
- ToString:
单独为 String 类型 定义了一个 ToString Trait,但是并不需要直接实现 ToString,而是实现了 fmt::Display 之后 就自动了提供了 ToString 中的to_string 方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: fmt::Display + ?Sized> ToString for T {
// A common guideline is to not inline generic functions. However,
// removing `#[inline]` from this method causes non-negligible regressions.
// See <https://github.com/rust-lang/rust/pull/74852>, the last attempt
// to try to remove it.
#[inline]
default fn to_string(&self) -> String {
use fmt::Write;
let mut buf = String::new();
buf.write_fmt(format_args!("{}", self))
.expect("a Display implementation returned an error unexpectedly");
buf
}
}
- FromStr & parse: 将String 转换为其他类型, 只需要实现了 FromStr for struct, 而String 中的parse 方法 只是 对FromStr::from_str(&string) 的调用
Expression: 程序是由一系列表达式组成的,
1) 赋值表达式 用; 结尾, 2) {} 也是表达式, 如果最后一个表达式 以; 结尾,则返回 (), 否则为最后一个表达式的 结果
Flow of control:
- if-else 也是表达式, 所有的分支必须返回同样的类型
loop: loop break continue。 break 用来随时中断退出loop, continue 则用于 用于 跳过剩下的 代码,重新开始一个 循环 loop 是可以嵌套的,并起名字, break ,以及continue 可以使用名字来进行 break, 或者continue loop 也是可以 返回数值的, 放到break 后面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
#![allow(unreachable_code)] fn main() { 'outer: loop { println!("Entered the outer loop"); 'inner: loop { println!("Entered the inner loop"); // This would break only the inner loop //break; // This breaks the outer loop break 'outer; } println!("This point will never be reached"); } println!("Exited the outer loop"); } fn main() { let mut counter = 0; let result = loop { counter += 1; if counter == 10 { break counter * 2; } }; assert_eq!(result, 20); }
- while
- for: for in 结构用来 遍历 所有实现了 IntoIterator 的对象, 比如简单 range形式: a..b, a..=b. for loop 会自动调用 into_iter 在参数上,我们可以主动产生下面几类实现IntoIterator 的 Iterator:
- iter: 产生 引用的 Iterator, 对 ownership不产生影响
- into_iter: 将ownership 交给 Iterator, 调用过之后的对象,将不再可用。产生
- iter_mut: 产生mut 引用的Iterator, 可以进行修改
match:
- c like 方式: 即 match number
- 解构对象:
- Tuples: 使用.. 来 忽略剩余所有的 tuple
- Enums:
- Pointers: * & ref ref mut 见下面示例
- Structs: struct 同样可以被match
- Guards: 在match 对象的arm中,使用 if 条件判断 即是 guards
- Bindings: match 在 arm中,除了解构对象的同时 可以将变量整体绑定 到一个变量上
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
fn main() {
let triple = (0, -2, 3);
// TODO ^ Try different values for `triple`
println!("Tell me about {:?}", triple);
// Match can be used to destructure a tuple
match triple {
// Destructure the second and third elements
(0, y, z) => println!("First is `0`, `y` is {:?}, and `z` is {:?}", y, z),
(1, ..) => println!("First is `1` and the rest doesn't matter"),
// `..` can be the used ignore the rest of the tuple
_ => println!("It doesn't matter what they are"),
// `_` means don't bind the value to a variable
}
}
//point s
fn main() {
// Assign a reference of type `i32`. The `&` signifies there
// is a reference being assigned.
let reference = &4;
match reference {
// If `reference` is pattern matched against `&val`, it results
// in a comparison like:
// `&i32`
// `&val`
// ^ We see that if the matching `&`s are dropped, then the `i32`
// should be assigned to `val`.
&val => println!("Got a value via destructuring: {:?}", val),
}
// To avoid the `&`, you dereference before matching.
match *reference {
val => println!("Got a value via dereferencing: {:?}", val),
}
// What if you don't start with a reference? `reference` was a `&`
// because the right side was already a reference. This is not
// a reference because the right side is not one.
let _not_a_reference = 3;
// Rust provides `ref` for exactly this purpose. It modifies the
// assignment so that a reference is created for the element; this
// reference is assigned.
let ref _is_a_reference = 3;
// Accordingly, by defining 2 values without references, references
// can be retrieved via `ref` and `ref mut`.
let value = 5;
let mut mut_value = 6;
// Use `ref` keyword to create a reference.
match value {
ref r => println!("Got a reference to a value: {:?}", r),
}
// Use `ref mut` similarly.
match mut_value {
ref mut m => {
// Got a reference. Gotta dereference it before we can
// add anything to it.
,*m += 10;
println!("We added 10. `mut_value`: {:?}", m);
},
}
}
fn main() {
struct Foo {
x: (u32, u32),
y: u32,
}
// Try changing the values in the struct to see what happens
let foo = Foo { x: (1, 2), y: 3 };
match foo {
Foo { x: (1, b), y } => println!("First of x is 1, b = {}, y = {} ", b, y),
// you can destructure structs and rename the variables,
// the order is not important
Foo { y: 2, x: i } => println!("y is 2, i = {:?}", i),
// and you can also ignore some variables:
Foo { y, .. } => println!("y = {}, we don't care about x", y),
// this will give an error: pattern does not mention field `x`
//Foo { y } => println!("y = {}", y),
}
}
fn main() {
let pair = (2, -2);
// TODO ^ Try different values for `pair`
println!("Tell me about {:?}", pair);
match pair {
(x, y) if x == y => println!("These are twins"),
// The ^ `if condition` part is a guard
(x, y) if x + y == 0 => println!("Antimatter, kaboom!"),
(x, _) if x % 2 == 1 => println!("The first one is odd"),
_ => println!("No correlation..."),
}
}
// A function `age` which returns a `u32`.
fn age() -> u32 {
15
}
fn main() {
println!("Tell me what type of person you are");
match age() {
0 => println!("I haven't celebrated my first birthday yet"),
// Could `match` 1 ..= 12 directly but then what age
// would the child be? Instead, bind to `n` for the
// sequence of 1 ..= 12. Now the age can be reported.
n @ 1 ..= 12 => println!("I'm a child of age {:?}", n),
n @ 13 ..= 19 => println!("I'm a teen of age {:?}", n),
// Nothing bound. Return the result.
n => println!("I'm an old person of age {:?}", n),
}
}
- if let: 在判断的同时 进行match
- while let
Functions:
- methods: 依附于 对象的函数, 在methods 的block中,能够 通过self使用 对象的 数据
- closures: |val| {val + x}
- Capturing: 捕获, 其可以 捕获环境中的 变量, 可以是: &T, &mut T , T(by value)
- 作为参数: 分为三类: Fn, FnMut, FnOnce, 对应ownership 的 &T, &mut T, T. Fn 可以无限次执行, FnMut 则要求 capture 变量的 mut 引用, FnOnce 则 只能执行一次
- 疑问:
- 如何 区分 Fn(i64, i64) -> i64 与Fn(&String) -> i64
- FnOnce 是如何确定的, 有些函数,即便将变量 move 到了block中, 然而依然可以调用多次, 这些又是如何判断的? 根据内部函数调用的 Fn 属性吗? 比如mem::drop(p) 则 包含其调用的函数 则为 FnOnce?
- 如何断定 Cloosure 为 Fn or FnMut ?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct Point {
x: f64,
y: f64,
}
// Implementation block, all `Point` methods go in here
impl Point {
// This is a static method
// Static methods don't need to be called by an instance
// These methods are generally used as constructors
fn origin() -> Point {
Point { x: 0.0, y: 0.0 }
}
// Another static method, taking two arguments:
fn new(x: f64, y: f64) -> Point {
Point { x: x, y: y }
}
}
Modules:
- mod visibility: mod 的可见性, mod 默认只能在本mod 可见, 需要使用 pub 来对外可见, 定义其中的fn, struct 使用同样的规则, 因为mod 可以nest, 所以 存在pub(self) (等同于private) pub(super) 即让super mod 可见, 而pub(crate)则让crate 可见
- struct visibility: struct 中包含fn 以及 field,默认都为 对 所在 定义的mod可见, pub 则开放为对外部的 mod可见
- mod vs struct: struct 的控制比较弱, mod的控制则相对复杂, struct 可能并不需要如此复杂的规则吧
- 关键字 use: 我们可以使用 use mod::struct as another_struct 来 减少路径的拼写, 使用as 更可以 启用别名
- mod 的 使用类似于 Unix下的 目录 安排, super 代表 .. self 则代表 本mod
Attributes: 可以用来作什么?
1) 条件编译, 2)设定crate 属性, 3)关闭 warning 4)启用编译器特性(maros etc) 5) link to a foreign library 6) 设定unit test
- 形式: 当应用到 整个crate: #![crate_attribute], 应用到module 或者 item : #[item_attribute]
- 还可以接受参数: 1) #[attribute = “value”] 2) #[attribute(key = “value”)] 3) #[attribute(value)]
示例:
- #[allow(dead_code)]: 关闭rust 关于没有调用函数的提示
- #![crate_name = “rary”]
- cfg(Configuration): 1) #[cfg(…)] 条件编译 2) cfg!(…) 在运行阶段的条件判断, 返回bool值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// This function only gets compiled if the target OS is linux
#[cfg(target_os = "linux")]
fn are_you_on_linux() {
println!("You are running linux!");
}
// And this function only gets compiled if the target OS is *not* linux
#[cfg(not(target_os = "linux"))]
fn are_you_on_linux() {
println!("You are *not* running linux!");
}
fn main() {
are_you_on_linux();
println!("Are you sure?");
if cfg!(target_os = "linux") {
println!("Yes. It's definitely linux!");
} else {
println!("Yes. It's definitely *not* linux!");
}
}
Smart Pointer:
- Box: heap store
- Rc
: Reference Counter - Rc::clone(&rc) || rc.clone()
- Rc::strong_count
- Rc::weak_count
OIBITs:
stand for “opt-in builtin trait”: 比如 Send, Sync, 该种 Trait 默认 为所有的 struct enum 提供默认实现, 即: 如果 struct A 中的field 都impl Trait 则 struct A 也同样 impl Trait。
impl Trait: 目前 可以在 fn xxx() -> impl Trait
- 带来了一系列的问题: 主要为 隐藏Trait 具体实现 + 避免 trait 带来的 dynamic dispatch, original proposal 在这里
- 由此 带出得一些列扩展有: 悬而未决的问题为: params impl Trait 是否 于 return impl Trait 一样, 没有dynamic dispatch 的代价呢? rfc 比较长, 英文阅读能力有待提高
- fn return impl Trait:链接
Box<Trait> vs Box<T> where T: Trait vs &Trait 的区别:
- Box<Trait>: 并非 Generic Type, 而是 Trait的 fat Pointer, 在 stack over flow 有明显的比较, 在 the rust book 中 在讲解了 Trait为 dynamic dispatch 的fat pointer & 其中可能包含 destructor 指针
- Box<T> where T: Trait 是 Generic Type
- 代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
// Box<T> where T: Trait use std::fmt::Debug; struct Wrapper<T> { contents: Option<Box<T>>, } impl<T: Debug> Wrapper<T> { fn new() -> Wrapper<T> { Wrapper { contents: None } } fn insert(&mut self, val: Box<T>) { } } fn main() { let mut w = Wrapper::new(); // makes T for w be an integer type, e.g. Box<i64> w.insert(Box::new(5)); // type error, &str is not an integer type // w.insert(Box::new("hello")); } // Box<Trait> use std::fmt::Debug; struct Wrapper { contents: Option<Box<Debug>>, } impl Wrapper { fn new() -> Wrapper { Wrapper { contents: None } } fn insert(&mut self, val: Box<Debug>) { } } fn main() { let mut w = Wrapper::new(); w.insert(Box::new(5)); w.insert(Box::new("hello")); }
dyn trait:
- (与 return impl Trait 完全不同) 为了区分 Box<Trait>, Box<Struct> 所以将 Box<Trait> -> Box<dyn Trait>, &Trait -> & dyn Trait, &mut Trait -> &mut dyn Trait
- dyn Trait 只是为了 解决 Box<Trait> vs Box<Struct> 的区分问题, 即: Box<Trait> 时候 需要添加 dyn 关键字 => Box<dyn Trait>
- 为什么能够是Sized? 参考
rust stackoverflow 问题: 使用 lldb target/debug/deps/smoke-910c8a3208aec5c7 ,跟gdb 调试一样,定义定位到 callstack的相关信息
rust mut var & mut ref 的含义
let mut a = xx; 是否 意味着 a 可以被替换为 其他的同类型的 var, 还是仅仅意味着 可以 a.xx 调用xx mut方法?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mut x: &mut i32 x = &y *x = 123
mut x: &i32 x = &y --------
x: &mut i32 ------ *x = 123
x: &i32 ------ --------
int *x;
cont int *x;
int * const x;
const int * const x;
#+end_center
#+begin_src rust
let mut ix = 10;
let mut iy = 20;
{
let mut x = &mut ix;
,*x = 100;
x = &mut iy;
,*x = 200;
}
println!("x: {}, iy: {}", ix, iy);
const & static
Rust 周边工具
rustup: rust complier 的管理工具, 可以方便的切换 stable, beta, and nightly
cargo: 是rust的包管理工具, 用来 下载 rust的依赖, 编译, 以及 分发 到 crates.io
在安装 rust 之后, cargo 也会被自动安装上,
cargo 提供了一些有用的工具有
- cargo new package # default –bin 生成 可执行 program, 可以pass –lib 来产生库程序
- cargo build
- cargo 存在的意义:
- 剥离 rustc 的复杂度, 类似 make 与 c 一样
- cargo 最终调用rustc 来编译 项目, 当然可以 直接使用 rustc 来编译项目,但是 需要出入 复杂的参数 来 添加项目 依赖关系, 编译文件, 依赖关系 等,并精心安排顺序 来进行调用。
- 所以使用cargo: make工具, cargo 的功能
- 使用两个文件 来 包含 package的信息
- 拉取,构建 package 依赖
- 使用正确的参数 来调用rustc 或者其他 tool, 来构建项目
- 引用约定,方便package 构建
- cargo 使用笔记:
- cargo new hello_world –bin
1 2 3 4 5 6 7 8
$ cd hello_world $ tree . . ├── Cargo.toml └── src └── main.rs 1 directory, 2 files其中 Cargo.toml 被称为 manifest,包含package的元数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
fn main() {
println!("Hello, world!");
}
$ cargo build
Compiling hello_world v0.1.0 (file:///path/to/package/hello_world)
$ ./target/debug/hello_world
Hello, world!
$ cargo run
Compiling hello_world v0.1.0 (file:///path/to/package/hello_world)
Running `target/debug/hello_world`
Hello, world!
$ cargo build --release
Compiling hello_world v0.1.0 (file:///path/to/package/hello_world)
- cargo build 将会 构建 package
- cargo run 则 构建并运行它
- cargo build –release 将 构建 优化的代码
- cargo 默认的构建 代码优化级别 为 debug, 存在的目录为 target/debug, 构建 优化后的代码需要 显式传递 参数 –release 生成的文件目录为 target/release
- Dependencies:
- crate.io 为 rust 中间的 package 机构, 用于发现 下载 更新package
- 添加依赖关系: 在 Cargo.toml 中 的dependencies 下,添加项目
1
2
3
4
5
6
7
8
9
[package]
name = "hello_world"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
[dependencies]
time = "0.1.12"
regex = "0.1.41"
- 之后的 cargo build 过程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ cargo build
Updating crates.io index
Downloading memchr v0.1.5
Downloading libc v0.1.10
Downloading regex-syntax v0.2.1
Downloading memchr v0.1.5
Downloading aho-corasick v0.3.0
Downloading regex v0.1.41
Compiling memchr v0.1.5
Compiling libc v0.1.10
Compiling regex-syntax v0.2.1
Compiling memchr v0.1.5
Compiling aho-corasick v0.3.0
Compiling regex v0.1.41
Compiling hello_world v0.1.0 (file:///path/to/package/hello_world)
- package 构成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
. ├── Cargo.lock ├── Cargo.toml ├── src/ │ ├── lib.rs │ ├── main.rs │ └── bin/ │ ├── named-executable.rs │ ├── another-executable.rs │ └── multi-file-executable/ │ ├── main.rs │ └── some_module.rs ├── benches/ │ ├── large-input.rs │ └── multi-file-bench/ │ ├── main.rs │ └── bench_module.rs ├── examples/ │ ├── simple.rs │ └── multi-file-example/ │ ├── main.rs │ └── ex_module.rs └── tests/ ├── some-integration-tests.rs └── multi-file-test/ ├── main.rs └── test_module.rs - Cargo.toml and Cargo.lock 在项目的根目录
- src 下 为源代码
- 默认的 library file 为 src/lib.rs
- 默认的 executable file 是 src/main.rc, 其他的 放在 src/bin/
- 基准测试 放在benches 目录下
- 示例代码放在examples 目录下
- 集成测试 放在 tests 目录下
- 其他详细的需要参看
- 默认的 library file 为 src/lib.rs
Cargo.toml 与 Cargo.lock: 两种目的
- cargo.toml 描述 大概的依赖关系 并不准确,是由 人来确定的
- Cargo.lock 包含准确的依赖关系, 由cargo 来维护
- 链接 示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Cargo.toml
[package]
name = "hello_world"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
[dependencies]
rand = { git = "https://github.com/rust-lang-nursery/rand.git", rev = "9f35b8e" }
Cargo.lock
[[package]]
name = "hello_world"
version = "0.1.0"
dependencies = [
"rand 0.1.0 (git+https://github.com/rust-lang-nursery/rand.git#9f35b8e439eeedd60b9414c58f389bdc6a3284f9)",
]
[[package]]
name = "rand"
version = "0.1.0"
source = "git+https://github.com/rust-lang-nursery/rand.git#9f35b8e439eeedd60b9414c58f389bdc6a3284f9"
- Cargo.lock 中包含 依赖的确定的 version, 当其他人使用的时候, 他们将使用相同的 sha,即便我们并没有在Cargo.toml 中使用
- cargo update 更新全部的依赖, cargo update -p rand 只更新依赖 rand
Test:
cargo test 执行 package中的所有test, test主要有两种: 1) 在每个 src 目录中的文件, 2) tests/ 目录下的所有文件。 1)中的为单元测试, 2)则为 集成测试,
1
2
3
4
5
6
7
8
$ cargo test
Compiling rand v0.1.0 (https://github.com/rust-lang-nursery/rand.git#9f35b8e)
Compiling hello_world v0.1.0 (file:///path/to/package/hello_world)
Running target/test/hello_world-9c2b65bbb79eabce
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
- cargo test foo 可以单独执行 名字为foo的测试,
- cargo test 其实还会执行 额外的测试,包含在 src中的部分 文档中的测试(并不重要,为补充部分)
Cargo Home: 当build package的时候, cargo 将下载的依赖package 存储到 Cargo home 下。当做cache 使用。
- 可以通过 改变 环境变量 CARGO_HOME 来改变 cargo home的值, 默认 为 $HOME/.cargo/
- Cargo Home 目录下的 数据:
- bin 目录: 可执行crate, 包括cargo install 或者 rustup 安装的
- git/db: crate 依赖git 项目时, cargo clone 项目到 该目录下
- git/checkouts: git/db 项目中的检出到该文件, 比如 依赖于特定的commit
- registry: 项目依赖于 crate.io 中的crate 存放在该目录下
- registry/index: crate 的原数据, 包括: version, dependencies 等
- registry/cache: 下载的crate 储存到该目录下, 存储形式为 .crate 的gzip压缩文件
- registry/src: cache 的解压形式 存放在 该文件中
- 指定 Dependencies:
- 依赖版本: 版本号各个位置数字的含义: link 与 link 中讲的同样, major.minor.patch
- Caret requirements: 指定 可以使用 一个 major 版本号 不变的更新, 但是 0 是 一个特殊的数字,标识不与 任何 数字兼容。即是: 0.0.1, 与 0.1.x 不兼容
下面为兼容样例:
1 2 3 4 5 6 7 8
^1.2.3 := >=1.2.3, <2.0.0 ^1.2 := >=1.2.0, <2.0.0 ^1 := >=1.0.0, <2.0.0 ^0.2.3 := >=0.2.3, <0.3.0 ^0.2 := >=0.2.0, <0.3.0 ^0.0.3 := >=0.0.3, <0.0.4 ^0.0 := >=0.0.0, <0.1.0 ^0 := >=0.0.0, <1.0.0
- Tilde requirements: 分为以下情况:
- 有 major.minor.patch 或者 major.minor 只有patch version 的升级是允许的
- 有major情况下, minor patch verison的升级 是允许的
- for example
1 2 3
~1.2.3 := >=1.2.3, <1.3.0 ~1.2 := >=1.2.0, <1.3.0 ~1 := >=1.0.0, <2.0.0
- Wildcard requirements:
- 允许所在位置上的 任何版本
- for example
1 2 3
\* := >=0.0.0 1.* := >=1.0.0, <2.0.0 1.2.* := >=1.2.0, <1.3.0
Workspaces: workspace 下的一系列package 共享同样的Cargo.lock, output dir, 多样的配置(比如profile), workspace下的packages 被称为 workspace members
- 存在两种形式: 1) [package] 与 [workspace] 共存在 Cargo.toml 中 2) 只有 [workspace] 存在Cargo.toml 中, 被称作 Virtual manifest
- 主要作用:
- 共享Cargo.lock
- 共享output dir, Cargo.toml 中 [target]
- [patch], [replace] and [profile.*] sections in Cargo.toml 只能在 识别 workspace 中的 manifest,member package 中的被忽略
- workspace section 中的配置
1
2
3
[workspace]
members = ["member1", "path/to/member2", "crates/*"]
exclude = ["crates/foo", "path/to/other"]
1
* members 为 成员package 列表, exclude 排除 member 4. workspace 寻找: Cargo 自动的 向上目录 寻找 含有 [workspace] 的Cargo.toml, 在 member中可以指定 package.workspace 来 直接指定 workspace 的位置, 来防止自动查找, 这个对于 没有在 workspace 目录下的member package 非常有用 5. member package: cargo commadn -p package 可以指定 package 来 执行命令, 如果没有指定 package, 则 选择当前所在目录的package, default-members = ["path/to/member2", "path/to/member3/"] 可以指定默认的 操作的member package