学习Rust遇到过的问题

Windows 上新安装 Rust 遇到过的问题

1. 首次 cargo run 运行时缺少 linker 文件

1
2
3
4
5
error: linker `link.exe` not found
note: The system cannot find the file specified. (os error 2)
note: the msvc targets depend on the msvc linker but `link.exe` was not found
note: please ensure that VS 2013, VS 2015 or VS 2017 was installed with the Visual C++ option
error: aborting due to previous error

解决方法:

  1. 通常登录 Rust 官网地址,选择安装时,会有相关提示,同时会给出一个下载 Build Tools 的地址,跳转后下载 Build Tools 文件即Visual Studio Installer,然后安装,注意,在安装界面选择 C++的那个组件即可,其他的组件不用安装,选择组件后,右侧会显示需要安装的模块,确保勾选了 VS xxx C++ build tools等与 C++ 有关的模块即可。安装后重启。

  2. 更多内容参考 ,StackOverflow 上第二个答案网上说也是可以的,没有尝试过。

vscode 使用注意事项

使用vscode + rust-analyzer 写rust 时,项目名不要命名为 rust 的关键字,否则会报错:

  • 自动补全功能不能使用,且会报错,
  • 例如,cargo new lifetime 那么该项目中就会报错
  • rust-analyzer 插件在vscode中的 快速修复 快捷键不能使用,
    • 解决:是因为与输入法的中英文标点切换的快捷键冲突了,关掉搜狗、微软输入法设置中的快捷键即可。

20211207

  1. 一个结点就能够引用其父结点,但不拥有其父结点。

    • 代码里不是传的引用吗?怎么就拥有了?
  2. 大部分情况下,所有权是非常明确的,可以准确的知道哪个变量拥有某个值。

  3. rust 有么有什么好用的编辑器?

    • vscode 默认使用代码提示功能有些弱,是否需要添加什么插件?
  4. match 表达式赋值?Rust 是怎样处理的?

  5. 看下面这段代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    use std::fs::File;
    use std::io::ErrorKind;

    fn main() {
    let f = File::open("hello.txt");

    let f = match f {
    Ok(file) => file,
    Err(error) => match error.kind() {
    ErrorKind::NotFound => match File::create("hello.txt") {
    Ok(fc) => fc,
    Err(e) => panic!("Problem creating the file: {:?}", e),
    },
    other_error => panic!("Problem opening the file: {:?}", other_error),
    },
    };
    }

    上面代码中 `other_error` 这个内部变量的作用与 `_` 一样吗?是可以代指余下所有的错误类型吗?
  6. 在 Rust 中,如果定义一个枚举,任何值只能是一个枚举的成员,且枚举的成员从根本上将仍是枚举,所以使用枚举中任何成员时,都应该看做是相同的类型。

    • 例如:IP地址要么是IPv4,要么是IPv6,只能是其中之一,且IPv4和IPv6都是IP地址,所以任何类型的IP地址都是看做相同的类型;
    • 举例2:Result<T,E>,无论是 Ok(T) ,还是 Err(E),它们本质上都是一种结果。所以在使用时,Ok 和 Err 是相同类型,即 Result。

引用

泛型、trait 与生命周期 章节的例子

1
2
3
4
5
6
7
8
9
10
11
fn largest(list: &[i32]) -> i32 {
let mut largest = list[0];

// 视频中说,这里相当于发生了解构,或者说是模式匹配?
for &item in list { // list 是切片,一种引用,item 为什么也是 &i32? 为什么 &item 之后,item 就变成 i32类型了? 如果解引用不应该使用 *item 为什么报错?
if item > largest {
largest = item;
}
}
largest
}

大于运算符(>)被定义在标准库中 trait std::cmp::PartialOrd 的一个默认方法。

  • PartialOrd 位于 prelude 中,不需要手动将其引入作用域。
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
[Error - 下午10:06:30] Request textDocument/completion failed.
Message: server panicked: called `Option::unwrap()` on a `None` value
Code: -32603
Panic context:
>
version: 0add6e95e 2021-12-20 stable
request: textDocument/completion CompletionParams {
text_document_position: TextDocumentPositionParams {
text_document: TextDocumentIdentifier {
uri: Url {
scheme: "file",
cannot_be_a_base: false,
username: "",
password: None,
host: None,
port: None,
path: "/c%3A/Tableware/codes/rust/minigrep/src/main.rs",
query: None,
fragment: None,
},
},
position: Position {
line: 3,
character: 43,
},
},
work_done_progress_params: WorkDoneProgressParams {
work_done_token: None,
},
partial_result_params: PartialResultParams {
partial_result_token: None,
},
context: Some(
CompletionContext {
trigger_kind: Invoked,
trigger_character: None,
},
),
}

thread '<unnamed>' panicked at 'called `Option::unwrap()` on a `None` value', C:\Users\runneradmin\.cargo\registry\src\github.com-1ecc6299db9ec823\chalk-solve-0.75.0\src\clauses\builtin_traits\fn_family.rs:91:55
stack backtrace:
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
1
2
3
4
5
6
7
8
9
2、下载安装racer(用于Rust代码自动补全):

cargo install racer

如果不成功,先将rustup更新成nightly版本,再进行下载:

rustup install nightly

cargo +nightly install racer

为什么 &String 类型可以传递给 &str 类型?Rust 内部做了转换?前面章节中好像有讲。

1
Cargo has produced no matching compilation artifacts.

20211229

看到19章时,关注一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
trait 内联类型的内容
写法1Self::Item
impl Iterator for Counter {
type Item = u32;

fn next(&mut self) -> Option<Self::Item> {


写法2,&T
impl<T> Deref for MyBox<T>{
type Target = T;

fn deref(&self) -> &T {
&self.0
}

通过联系多理解一下,引用在函数和方法中的使用

1
2
3
fn hello(name: &str) {

fn deref(&self)

对于Rust 的模块路径使用还不太熟

use xxx(package名)::Post(pub 的struct名)

为什么能直接只用 use crate::Post?

或者 use crate::xxx(package名)::Post(pub 的 struct 名)???

lib.rs 文件中的顶级项,在其他文件中引用是不是路径都是 use xxx(package名)::bbb 的形式?

所以也就是为什么 Rust 的 crate 中只能有一个lib.rs 的原因(只能给一个为命名的文件启动添加名字?)?

  • 在同一个文件内部使用定义在文件内部的struct、enum、trait 时,使用 use crate::xxx::xxx的形式;

  • 在跨文件中使用其他文件中定义的 struct、enum、trait时,使用use pakcage名::xxx::xxx 的形式;

  • 当文件名本身为mod.rs时,此时若用到该文件中的定义项时,package的名称即为该mod的名称,

    • 例如

      1
      2
      3
      4
      5
      6
      7
      8
      |-linked-list
      |--main.rs
      |--lib.rs
      |--mod.rs

      那么此时如果需要在lib.rs中使用mod.rs中定义的enum List,
      则为
      use linked-list::List::Cons;

何时使用if let模式匹配?

什么场景下使用?为什么不直接使用简单的赋值语句?有什么好处?

或者说当只关心一个值得时候,使用 if let 模式匹配比使用if有哪些好处吗?

  • 更像 Rust 风格?
1
2
3
4
5
pub fn request_review(&mut self) {
if let Some(s) = self.state.take() {
self.state = Some(s.request_review())
}
}

例如,上面代码为什么不是

1
self.state = self.state.unwrap().request_review() ?或者严谨写添加一个if 判断

创建类型别名,使用 type 新类型=原类型; 还是使用 as

  • 使用 type语法

下列代码为什么可以?

1
2
3
4
5
6
7
8
9
10
// 方法调用
let stream = stream.unwrap();
thread::spawn(|| {
handle_connection(stream);
});
...

// 方法声明
fn handle_connection(mut stream: TcpStream) {

方法不是声明mut的参数吗?为什么调用时传递不可变也可以?

& 和 ref,解构和解引用

  1. & 和 ref 的区别,分别在什么场景中使用;
  2. 解构和解引用分别在什么场景中使用;

& 和 ref

涉及到 ref 的内容,我们通常是在 match 表达式中使用。

默认情况下,match 表达式会消耗匹配中的值,即匹配到的值的所有权会发生转移;

这种情况下,后面就不能再次使用这些值。因为被匹配的值的所有权已经发生move

如果匹配的类型实现了 Copy trait,则不会发生move,而是 copy。不会出现上面的问题。

当后面需要使用该值的情况下,可能发生的遇到的问题,

  • 怎么解决?
  • 使用ref

ref 使用在模式匹配中,可以使其变为借用(borrow)而不是move

ref 使用在 match 中,并不会影响值是否被匹配,只会影响匹配的方式;

样例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 默认情况下的情况
let maybe_name = Some(String::from("Alice"));
// The variable 'maybe_name' is consumed here ...
match maybe_name {
Some(n) => println!("Hello, {}", n),
_ => println!("Hello, world"),
}
// ... and is now unavailable.
println!("Hello again, {}", maybe_name.unwrap_or("world".into()));


// 使用了 `ref` 关键字的效果
let maybe_name = Some(String::from("Alice"));
// Using `ref`, the value is borrowed, not moved ...
match maybe_name {
Some(ref n) => println!("Hello, {}", n),
_ => println!("Hello, world"),
}
// ... so it's available here!
println!("Hello again, {}", maybe_name.unwrap_or("world".into()));

& vs ref

  • ref ,表示我们仅引用该值,
  • &,表示我们期望使用一个对象的引用,
    • &FooFoo 是不一样的。

ref - Rust (rust-lang.org)

关键字 ref 与 主类型 reference 的区别

二进制有符号整数的表示

-1 ?

最小值如何表示为二进制?

Iter 与 FnMut 的问题

Iter<i32> 为什么可以调用 Iterator::any<F>(&mut self, f: F)

any 的第一个参数不是 &mut self 吗?不是需要可变借用才能调用吗?

例如:

1
2
3
4
5

let vec1 = vec![1, 2, 3];

// `iter()` for vecs yields `&i32`. Destructure to `i32`.
println!("2 in vec1: {}", vec1.iter() .any(|&x| x == 2));

trait 的可见性?

trait 是否与 enum 一样,只需要对整体进行 pub设置,所有方法都是可见的pub?

基础数据类型的类型转换?

1
2
3
4
5
fn to_centimeters(&self) -> Centimeters {
let &Inches(inches) = self;

Centimeters(inches as f64 * 2.54)
}

如上所示,先通过解构获得 inches变量,然后i32 * f64 ,这里进行了类型转换 f64 * f64

所以在Rust 中,基础类型的类型转换也是需要我们自己做的是吗?

collect 指定类型的写法

1
assert_eq!(doubles.collect::<Vec<i32>>(), vec![4, 6]);

如上,指定了Vec<i32>类型,这种写法的语法规则在哪里?或者查一下collect 的官方文档说明。

答:这是 turbofish 写法,因为 collect() 方法是泛型方法,使用时支持转换为多种类型,使用时,要么在定义时显示声明变量类型,要么使用 turbofish 语法,显示表示collect() 返回的类型。

Error 之间的区别

mismatched types
expected struct Box<dyn std::error::Error>
found struct std::io::Error
for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html

看一下错误中提到的内容文档

写 Rust CLI程序需要考虑的问题

  1. 如何处理error?

    • 是返回给调用者 result Result?还是触发 panic ?

      • return Error,使用 match 模式匹配?使用 ? 符号?

        • Error 是 Result 的一个枚举分支;

        • 如果使用 ? 记得添加正常返回值Ok(xxx) 部分的内容;

        • 使用默认返回的 Error 信息?还是自定义错误信息?当默认返回的错误信息无法囊括程序可能遇到的错误时,建议自定义struct,

          • 可以使用 map_err() 映射函数

          • 建议使用 anyhow 第三方库。

            • 可以自定义错误信息,同时保留原始错误
      • panic,使用 match 模式匹配?还是 unwrap() ? 或是 expect() 指定以 panic 内容?

如何运行工作空间子项目CLI的程序?

1
cargo run -p grrs -- 123 test.txt

解释:-p 表示工作空间中的模块,-- 后面的为 grrs 程序支持的命令行参数

执行样例报错

执行 [CLI样例](Testing - Command Line Applications in Rust (rust-cli.github.io))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use assert_fs::prelude::*;

#[test]
fn find_content_in_file() -> Result<(), Box<dyn std::error::Error>> {
let file = assert_fs::NamedTempFile::new("sample.txt")?;
file.write_str("A test\nActual content\nMore content\nAnother test")?;

let mut cmd = Command::cargo_bin("grrs")?;
cmd.arg("test").arg(file.path());
// 从这里开始引发错误,为什么?
cmd.assert()
.success().stdout(predicate::str::contains("test\nAnother test"));

Ok(())
}

报错结果如下:查一下为什么会出现test.txt ?

1
2
3
4
5
6
7
8
9
running 1 test
thread 'find_content_in_file' panicked at 'Unexpected failure.
code-1
stderr=```"Error: could not read file `test.txt`\n\nCaused by:\n 系统找不到指定的文件。 (os error 2)\n"```
command=`"C:\\Codes\\rust-study-codes\\target\\debug\\grrs.exe" "test" "C:\\Users\\spoon\\AppData\\Local\\Temp\\.tmpKNow85\\sample.txt"`
code=1
stdout=""
stderr="Error: could not read file `test.txt`\n\nCaused by:\n 系统找不到指定的文件。 (os error 2)\n"
', /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b\library\core\src\ops\function.rs:227:5

所有权与 Copy,Clone

变量的所有权总是遵循相同的模式:

  • 将值赋值给另一个变量时移动它。

    • 如果值的类型实现了clone这个函数,可以通过复制该值给新变量。此时在堆上是有两个值。

      也就是使用时,如果方法需要的是获取所有权,而传参是引用时,可以通过调用 clone() 复制该值,然后传递。

      1
      2
      3
      4
      let pre_block: &Block = xxx;
      Block::new("", pre_block.clone());
      // Block::new() 的定义如下
      pub fn new(data: &str, pre_block: Block)
  • 当持有堆中数据值的变量离开作用域时,其值将通过drop 被清理掉,除非数据所有权移动给了另一个变量所有。

  • 实现了Copy trait 的数据类型,赋值后所有权并不会移动。

    • 例如,常用的标量类型,分配到栈中。
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2022-2023 ligongzhao
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信