应用篋:方法借用实例

学习内容

  • 了解和学习Rust语言方法借用实例

篇目

实现复制特质Copy类型的借用实例

  在下面程序方法main()的三段代码里,表面上并没看到复制变量num,但是实际上存在变量num的复制。一旦调用方法fn_borrow(),方法内部就进行了复制特质Copy的复制。这是怎么知道的呢?


# #![allow(unused_variables)]
#fn main() {
    // File: lib-hello/src/immut/kw_fn/mod.rs
    // Function use_kw_fn_u8()

    fn fn_borrow(_: u8) {}

    let num = 42;
    fn_borrow(num);

    dbg!(num);
    
#}

  下面程序代码,说明了上面问题。该程序在上面程序基础上,把变量num在调用方法之前之后以及在方法内的内存地址都打印出来。


# #![allow(unused_variables)]
#fn main() {
    // File: lib-hello/src/immut/kw_fn/mod.rs
    // Function use_kw_fn_u8_ref()

    fn fn_borrow(num: u8) {
        println!("inside fn = {:p}", &num);
    }

    let num = 42;
    println!("Before fn = {:p}", &num);
    fn_borrow(num);
    println!("After fn = {:p}", &num);

    dbg!(num);

#}

  这是上面程序输出结果。从结果可以看到,在调用方法之前之后变量num内存地址是完全一样的,只是在方法内变量num内存地址是不一样的。这说明方法内部从一开始就复制了变量num,这是因为类型u8复制特质`Copy,所以可以存在这样借用机制。

[bin-hello/examples/use_kw_fn_u8.rs:17] num = 42
───────┬─────────────────────────────────────────────────────────────────────────
       │ STDIN
───────┼─────────────────────────────────────────────────────────────────────────
   1   │ Before fn = 0x7fff531fd04f
   2   │ inside fn = 0x7fff531fcf57
   3   │ After fn = 0x7fff531fd04f
───────┴─────────────────────────────────────────────────────────────────────────

未实现复制特质Copy类型的借用实例


# #![allow(unused_variables)]
#fn main() {
    // File: ./bin-hello/examples/kw_fn/vec_u8/mod.rs
    // #[cfg(feature = "err_01")]

    fn fn_borrow(vec_u8s: Vec<u8>) {
        println!("Inside fn = {:p}", &vec_u8s);
    }

    let vec_instance: Vec<_> = vec![33, 42];
    println!("Before fn = {:p}", &vec_instance);
    fn_borrow(vec_instance);
    println!("After fn = {:p}", &vec_instance);

    dbg!(vec_instance);

#}

  在下面的程序里,在执行该程序以后,可以看到错误提示信息,这是因为类型Vec<u8>没有实现复制特质Copy,而方法fn_borrow里面还是执行了变量的复制,相当于执行了下面一行代码:

let vec_u8s = vec_instance;

  这是上面程序输出错误结果:

error[E0382]: borrow of moved value: `vec_instance`
  --> bin-hello/examples/kw_fn_vec_u8.rs:32:22
   |
29 |     let vec_instance = Vec::new();
   |         ------------ move occurs because `vec_instance` has type `std::vec::Vec<u8>`, which does not implement the `Copy` trait
30 |     println!("{:p}", &vec_instance);
31 |     fn_borrow(vec_instance);
   |               ------------ value moved here
32 |     println!("{:p}", &vec_instance);
   |                      ^^^^^^^^^^^^^ value borrowed here after move

  上面程序类型Vec<u8>是可以实现复制特质Copy,但是怎么样可以简单这种功能实现呢?

借用机制代码实例


# #![allow(unused_variables)]
#fn main() {
    // File: ./bin-hello/examples/kw_fn/vec_u8/mod.rs
    // #[cfg(feature = "ok")]

    fn fn_borrow(vec_u8s: &Vec<u8>) {
        println!("Inside fn = {:p}", &vec_u8s);
    }

    let vec_instance: Vec<_> = vec![33, 42];
    println!("Before fn = {:p}", &vec_instance);
    fn_borrow(&vec_instance);
    println!("After fn = {:p}", &vec_instance);

    dbg!(vec_instance);

#}

  上面程序代码,使用了Rust语言的借用机制,实现了变量的借用,以达到传递变量值到函数或者方法。在Rust语言内部,存在一行下面代码,通过传递类型Vec的引用对象&vec_instance到方法fn_borrow(),以实现这种借用机制:

let vec_u8s = &vec_instance;

题外话

Rust语言下横杆_

  在上面程序中,有两个地方存在下横杆_,这是Rust语言待定符号。比如,下面代码,要是函数或者方法参数还没有确定如何使用,就可以使用这个待定符号下横杆_

fn fn_borrow(_: u8) {}

  下面是另外一个代码实例,我们知道,接下来该变量会存在类型定义,如作为方法参数使用该变量,也可以使用这个待定符号下横杆_。但是要是没有作为方法参数和其他使用该变量,编译器就会报错。

let vec_instance :Vec<_> = vec![33, 42];

向量类型完整定义方法


# #![allow(unused_variables)]
#fn main() {
    // File: ./bin-hello/examples/kw_fn/vec_u8/mod.rs
    // #[cfg(feature = "cp")]

    fn fn_borrow(vec_u8s: &Vec<u8>) {
        println!("Inside fn = {:p}", &vec_u8s);
    }

    let mut vec_instance: Vec<u8> = Vec::<u8>::new();
    vec_instance.push(33);
    vec_instance.push(42);

    println!("Before fn = {:p}", &vec_instance);
    fn_borrow(&vec_instance);
    println!("After fn = {:p}", &vec_instance);

    dbg!(vec_instance);

#}

  上面程序方法`main()`第二段的第一行代码,是类型向量最完整的表达形式,它也可以简写为下面这种常见的一行代码。同时也要注意到,前面代码里方法参数也是简写形式,而上面程序代码也是完整形式。这样定义对象变量的类型Vec::<u8>形式,与方法参数定义的引用类型&Vec::<u8>形式具有其一致性。

let mut vec_instance = Vec::new();

向量宏vec!

  在前面程序代码里,可以看到类型向量宏vec!,这是创建其对象的宏,它类似于打印宏println!。上面一行宏vec!代码,相当于下面的三行代码。当然其中不同的是,下面类型向量对象是可变绑定方式,而前面的向量对象是不可变绑定方式。Rust语言为我们创建类型向量对象提供了两种不同的方式:不可变和可变的向量绑定对象。


# #![allow(unused_variables)]
#fn main() {
    let mut vec_instance: Vec<u8> = Vec::<u8>::new();
    vec_instance.push(33);
    vec_instance.push(42);
#}

  下面我们把上面代码整理如下,目的便于与类型字符串进行比较:


# #![allow(unused_variables)]
#fn main() {
    // File: lib-hello/src/mutable/mut_new/mod.rs
    // Function use_vec_mut_new()

    let immut_vec: Vec<_> = vec![33, 42];
    dbg!(immut_vec);

    let mut mut_vec: Vec<u8> = Vec::<u8>::new();
    mut_vec.push(33);
    mut_vec.push(42);
    dbg!(mut_vec);

#}

  其实,类型字符串也有同样的功能,只是表达方式不同而已:


# #![allow(unused_variables)]
#fn main() {
    // File: lib-hello/src/mutable/mut_new/mod.rs
    // Function use_string_mut_new()

    let immut_string = String::from("Hello");
    dbg!(immut_string);

    let mut mut_string = String::new();
    mut_string.push_str("Hello");
    dbg!(mut_string);

#}

参考资料