Post

rust

rust

Rust基础 知识:

std::fmt:

  1. format! 的使用 方法: positional params:, named params: , formating params:
  2. 示例

    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);
    
  3. formating trait: 实践显式 对于外部的Type {}确实需要impl Display, 而{:?} 需要 impl Debug
    • {} => Display
    • => Debug
    • => Octal
    • => Pointer
  4. fmt::Display vs fmt::Debug
    • Display: 断言 实现者 总是返回 UTF-8 的字符串, 并非所有的 都实现了 Display * Debug: 应该为所有pub type 实现, 输出为 内部状态, 该Trait 的目的是为了方便Rust Debug, 可以 使用#[derive(Debug)] 来使用默认的内部实现

array & Slice

  1. array的类型为: [T: len], let mut array: [i32; 3] = [0; 3];
  2. Slice: [T]

structures: 1) Tuple struct, 2) classic struct 3) Unit structs

  1. struct Pair(i32, f32);
  2. struct Person { name: String, age: u8}
  3. 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:

  1. From 为一个类型定义, 如何create self 从 另一个type中转变
  2. 则是From 的 调用者, From for U 自动实现了 Into\<U\> for T, blank implement

TryFrom & TryInto:

类似于 From & Into 不同的是, 转换可能失败,返回Result

ToString & FromStr:

  1. 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
     }
 }
  1. FromStr & parse: 将String 转换为其他类型, 只需要实现了 FromStr for struct, 而String 中的parse 方法 只是 对FromStr::from_str(&string) 的调用

Expression: 程序是由一系列表达式组成的,

1) 赋值表达式 用; 结尾, 2) {} 也是表达式, 如果最后一个表达式 以; 结尾,则返回 (), 否则为最后一个表达式的 结果

Flow of control:

  1. if-else 也是表达式, 所有的分支必须返回同样的类型
  2. 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);
    }
    
    
  3. while
  4. for: for in 结构用来 遍历 所有实现了 IntoIterator 的对象, 比如简单 range形式: a..b, a..=b. for loop 会自动调用 into_iter 在参数上,我们可以主动产生下面几类实现IntoIterator 的 Iterator:
    1. iter: 产生 引用的 Iterator, 对 ownership不产生影响
    2. into_iter: 将ownership 交给 Iterator, 调用过之后的对象,将不再可用。产生
    3. iter_mut: 产生mut 引用的Iterator, 可以进行修改

match:

  1. c like 方式: 即 match number
  2. 解构对象:
    1. Tuples: 使用.. 来 忽略剩余所有的 tuple
    2. Enums:
    3. Pointers: * & ref ref mut 见下面示例
    4. Structs: struct 同样可以被match
  3. Guards: 在match 对象的arm中,使用 if 条件判断 即是 guards
  4. 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),
      }
  }
  1. if let: 在判断的同时 进行match
  2. while let

Functions:

  1. methods: 依附于 对象的函数, 在methods 的block中,能够 通过self使用 对象的 数据
  2. closures: |val| {val + x}
    • Capturing: 捕获, 其可以 捕获环境中的 变量, 可以是: &T, &mut T , T(by value)
    • 作为参数: 分为三类: Fn, FnMut, FnOnce, 对应ownership 的 &T, &mut T, T. Fn 可以无限次执行, FnMut 则要求 capture 变量的 mut 引用, FnOnce 则 只能执行一次
    • 疑问:
      1. 如何 区分 Fn(i64, i64) -> i64 与Fn(&String) -> i64
      2. FnOnce 是如何确定的, 有些函数,即便将变量 move 到了block中, 然而依然可以调用多次, 这些又是如何判断的? 根据内部函数调用的 Fn 属性吗? 比如mem::drop(p) 则 包含其调用的函数 则为 FnOnce?
      3. 如何断定 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:

  1. mod visibility: mod 的可见性, mod 默认只能在本mod 可见, 需要使用 pub 来对外可见, 定义其中的fn, struct 使用同样的规则, 因为mod 可以nest, 所以 存在pub(self) (等同于private) pub(super) 即让super mod 可见, 而pub(crate)则让crate 可见
  2. struct visibility: struct 中包含fn 以及 field,默认都为 对 所在 定义的mod可见, pub 则开放为对外部的 mod可见
  3. mod vs struct: struct 的控制比较弱, mod的控制则相对复杂, struct 可能并不需要如此复杂的规则吧
  4. 关键字 use: 我们可以使用 use mod::struct as another_struct 来 减少路径的拼写, 使用as 更可以 启用别名
  5. 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)]
示例:
  1. #[allow(dead_code)]: 关闭rust 关于没有调用函数的提示
  2. #![crate_name = “rary”]
  3. 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:

  1. Box: heap store
  2. 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 提供了一些有用的工具有
  1. cargo new package # default –bin 生成 可执行 program, 可以pass –lib 来产生库程序
  2. cargo build
  3. cargo 存在的意义:
    • 剥离 rustc 的复杂度, 类似 make 与 c 一样
    • cargo 最终调用rustc 来编译 项目, 当然可以 直接使用 rustc 来编译项目,但是 需要出入 复杂的参数 来 添加项目 依赖关系, 编译文件, 依赖关系 等,并精心安排顺序 来进行调用。
    • 所以使用cargo: make工具, cargo 的功能
      1. 使用两个文件 来 包含 package的信息
      2. 拉取,构建 package 依赖
      3. 使用正确的参数 来调用rustc 或者其他 tool, 来构建项目
      4. 引用约定,方便package 构建
  4. 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 下 为源代码
    1. 默认的 library file 为 src/lib.rs
      1. 默认的 executable file 是 src/main.rc, 其他的 放在 src/bin/
      2. 基准测试 放在benches 目录下
      3. 示例代码放在examples 目录下
      4. 集成测试 放在 tests 目录下
      5. 其他详细的需要参看

Cargo.toml 与 Cargo.lock: 两种目的

  1. cargo.toml 描述 大概的依赖关系 并不准确,是由 人来确定的
  2. Cargo.lock 包含准确的依赖关系, 由cargo 来维护
  3. 链接 示例:
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 目录下的 数据:
    1. bin 目录: 可执行crate, 包括cargo install 或者 rustup 安装的
    2. git/db: crate 依赖git 项目时, cargo clone 项目到 该目录下
    3. git/checkouts: git/db 项目中的检出到该文件, 比如 依赖于特定的commit
    4. registry: 项目依赖于 crate.io 中的crate 存放在该目录下
      • registry/index: crate 的原数据, 包括: version, dependencies 等
      • registry/cache: 下载的crate 储存到该目录下, 存储形式为 .crate 的gzip压缩文件
      • registry/src: cache 的解压形式 存放在 该文件中
    5. 指定 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
        
    6. 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
        
    7. 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. 存在两种形式: 1) [package] 与 [workspace] 共存在 Cargo.toml 中 2) 只有 [workspace] 存在Cargo.toml 中, 被称作 Virtual manifest
  2. 主要作用:
    1. 共享Cargo.lock
    2. 共享output dir, Cargo.toml 中 [target]
    3. [patch], [replace] and [profile.*] sections in Cargo.toml 只能在 识别 workspace 中的 manifest,member package 中的被忽略
  3. 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
This post is licensed under CC BY 4.0 by the author.