当内联的相同代码没有时,为什么闭包会引入借位?

时间:2017-06-08 18:14:21

标签: rust closures lifetime

此代码有效:

extern crate num;

use num::{BigInt, FromPrimitive, Zero};

fn sample() {
    let mut to_factor = "600851475143".parse::<BigInt>().unwrap();

    let mut prime = BigInt::from_i32(2).unwrap();

    let zero = BigInt::zero();

    //let is_div = |n, p| { n % p == zero};

    loop {
        if &to_factor % &prime == zero {
            to_factor = &to_factor / &prime;
        } else {
            break;
        }
    }

    println!("{}", to_factor);
}

但是如果我尝试用闭包替换循环中的条件,它就不再编译了:

fn sample() {
    let mut to_factor = "600851475143".parse::<BigInt>().unwrap();

    let mut prime = BigInt::from_i32(2).unwrap();

    let zero = BigInt::zero();

    let is_div = |n, p| { n % p == zero};

    loop {
        if is_div(&to_factor, &prime) {
            to_factor = &to_factor / &prime;
        } else {
            break;
        }
    }

    println!("{}", to_factor);
}

错误如下:

error[E0506]: cannot assign to `to_factor` because it is borrowed
  --> src/main.rs:16:13
   |
15 |         if is_div(&to_factor, &prime) {
   |                    --------- borrow of `to_factor` occurs here
16 |             to_factor = &to_factor / &prime;
   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `to_factor` occurs here

对我来说似乎没问题 - 当我们改变to_factor时,借用应该“超过”,当然?

如果我用实际函数替换is_div来增加混淆:

fn is_div(n: &BigInt, p: &BigInt) -> bool {
    return to_factor % p == BigInt::zero()
}

它工作正常。

我是Rust的初学者,但不是一般的编程。我很确定这与所有权有关,但它也可能与闭包的实现方式有关?

这些示例是我尝试编写的实际代码中的MWE。它们在这一点上并没有真正意义,但它们表现出相同的编译错误。

2 个答案:

答案 0 :(得分:4)

您正在遇到Rust类型系统的实现限制,特别是关于闭包参数的类型推断。也就是说,当你声明一个与立即使用的闭包相比没有立即使用的闭包时,推断的类型会略有不同。

此闭包有效,因为类型推断可以立即将参数连接到参数:

loop {
    if (|n, p| n % p == zero)(&to_factor, &prime) {
        to_factor = &to_factor / &prime;
    } else {
        break;
    }
}

这也有效,因为我们立即定义了参数的类型:

let is_div = |n: &BigInt, p: &BigInt| n % p == zero;

loop {
    if is_div(&to_factor, &prime) {
        to_factor = &to_factor / &prime;
    } else {
        break;
    }
}

这也是您的功能版本有效的原因。

问题跟踪器上有很多问题(搜索起来非常困难!),但12679是较旧的问题。

这是您经常看到内联定义的闭包的原因。

答案 1 :(得分:0)

正如你所写的那样,is_div闭包不会借用to_factor,它会永远占用它。如果您将闭包签名更改为借用to_factor,则可以正常工作。

let is_div = |&n, p| { n % p == zero};

不能在操场上使用板条箱,但这说明了它。

fn main() {
    let mut to_factor = "654684234".parse::<i32>().unwrap();

    let prime = 2;

    let zero = 0;

    let is_div = |&n, p| n % p == zero;

    loop {
        if is_div(&to_factor, &prime) {
            to_factor = &to_factor / &prime;
        } else {
            break;
        }
    }

    println!("{}", to_factor);
}