最近,在使用
Rust
时遇到了
Reborrow
的概念,记录下来以备以后参考。

1. 起因

起因准备对数据进行
Min-Max
标准化处理,也就是将一系列数据映射到一个新的范围。

首先,需要遍历数据,找出其中的
最大值

最小值
,然后通过公式改变原始数据集的值。

Min-Max
公式:标准化后的值 = (原始值 - 最小值) / (最大值 - 最小值)

简化后的代码如下:

fn main() {
    let mut values = vec![10.5, 22.3, 103.5, 45.75];
    let v = &mut values;
    println!("原始数据: {:#?}", v);

    let mut max = f64::MIN;
    let mut min = f64::MAX;

    for n in v {
        if *n > max {
            max = *n;
        }
        if *n < min {
            min = *n;
        }
    }

    println!("max is {}", max);
    println!("min is {}", min);

    println!("开始Min-Max标准化处理...");
    for n in v {
        *n = (*n - min) / (max - min);
    }

    println!("处理后数据: {:#?}", values);
}

运行时有如下错误:

error[E0382]: use of moved value: `v`                                                                     
   --> src/main.rs:22:14
    |
3   |     let v = &mut values;
    |         - move occurs because `v` has type `&mut Vec<f64>`, which does not implement the `Copy` trai
t
...
9   |     for n in v {
    |              - `v` moved due to this implicit call to `.into_iter()`
...
22  |     for n in v {
    |              ^ value used here after move
    |

大概是
第9行
遍历
v
的找出最大值和最小值时候,
可变借用v
的使用权已经转移了,

所以在
第22行

再次遍历v
去修改值的时候,出现错误。

这里,因为
Vector
没有实现
Copy Trait
,所以它的可变借用在第一次遍历时,由于隐式的调用了
.into_iter()
,所有权发生了转移。

如果想多次遍历
Vector
,可以使用它的不可变借用,比如定义
let v = &values;

那么,就可以多次遍历
v
,因为不可变借用都实现了
Copy Trait

但是,我第二次遍历
v
的时候,还需要修改其中的值,所以必须定义为可变借用
let v = &mut values;

通过查询资料,发现
Reborrow
的机制可以实现上面的需求。

2. Reborrow概念

借用(
Borrow
)是
Rust
中的一个重要概念,它是允许代码访问某个值而不获取其所有权的一种机制。


Reborrow
则是指在一个已存在的借用基础上创建一个新的借用,

这个新的借用可以是不可变的,也可以是可变的(前提是原始借用是可变的,并且没有其他借用存在)。

总的来说,
Reborrow
通过在已存在的借用上创建新的借用,从而扩展引用的生命周期并在更广泛的作用域内安全地访问值。

3. 解决方法

下面通过实践来检验对
Reborrow
概念的理解。

回到第一节中遇到的问题,解决方式就是在第一次遍历
v
时(
第9行
),不要把所有权转移出去,

这样,第二次遍历
v

第22行
)的时候,就不会报出
"value used here after move"
的错误。

根据
Reborrow
的机制,我们在
第9行
可以
Reborrow
可变借用
v
,这样转移出去的是被再次借用的
v
,而不是
v
本身。

改变方法很简单,
第9行
改为
for n in &*v {
即可,也就是先
还原v
(
*v
),然后
Reborrow
(
&*v
)。

修改后再次运行代码:

$  cargo run

原始数据: [
    10.5,
    22.3,
    103.5,
    45.75,
]
max is 103.5
min is 10.5
开始Min-Max标准化处理...
处理后数据: [
    0.0,
    0.12688172043010754,
    1.0,
    0.3790322580645161,
]

values
中的数据可以正常转换了。

注意,这里是将
v
Reborrow成一个不可变借用
&*v
,因为我第一次遍历时不需要改变
v

如果想
v
Reborrow成一个可变借用,可以写成:
&mut *v

标签: none

添加新评论