为什么二元运算符的结果需要适当的生命周期?

时间:2017-10-20 22:02:44

标签: rust

这与my earlier question关于使模幂运算方法通用有关。我现在已经得到了以下代码:

fn powm<T>(fbase: &T, exponent: &T, modulus: &T) -> T
where
    T: Mul<T, Output = T>
        + From<u8>
        + PartialEq<T>
        + Rem<T, Output = T>
        + Copy
        + for<'a> Rem<&'a T, Output = T>
        + Clone
        + PartialOrd<T>
        + ShrAssign<T>,
    for<'a> &'a T: PartialEq<T> + Rem<&'a T, Output = T>,
{
    if modulus == T::from(1) {
        T::from(0)
    } else {
        let mut result = T::from(1);
        let mut base = fbase % modulus;
        let mut exp = exponent.clone();
        while exp > T::from(0) {
            if exp % T::from(2) == T::from(1) {
                result = (result * base) % modulus;
            }
            exp >>= T::from(1);
            base = (base * base) % modulus;
        }
        result
    }
}

我的理解是,通过定义特征绑定where for<'a> &'a T: Rem<&'a T, Output=T>,可以理解我可以在类型%的两个操作数上使用模运算符&'a T,结果将是类型为T。但是,我收到以下错误:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/main.rs:20:30
   |
20 |         let mut base = fbase % modulus;
   |                              ^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #3 defined on the function body at 3:1...
  --> src/main.rs:3:1
   |
3  | / fn powm<T>(fbase: &T, exponent: &T, modulus: &T) -> T
4  | | where
5  | |     T: Mul<T, Output = T>
6  | |         + From<u8>
...  |
30 | |     }
31 | | }
   | |_^
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:20:32
   |
20 |         let mut base = fbase % modulus;
   |                                ^^^^^^^
note: but, the lifetime must be valid for the anonymous lifetime #1 defined on the function body at 3:1...
  --> src/main.rs:3:1
   |
3  | / fn powm<T>(fbase: &T, exponent: &T, modulus: &T) -> T
4  | | where
5  | |     T: Mul<T, Output = T>
6  | |         + From<u8>
...  |
30 | |     }
31 | | }
   | |_^
note: ...so that types are compatible (expected std::ops::Rem, found std::ops::Rem<&T>)
  --> src/main.rs:20:30
   |
20 |         let mut base = fbase % modulus;
   |                              ^

如果我用

替换有问题的行,代码就可以了
let mut base = fbase.clone() % modulus;

如果我可以使用modulo运算符返回&#34; fresh&#34;我不知道为什么我需要首先克隆。类型T的元素。我需要修改我的特质界限吗?为什么会出错?

1 个答案:

答案 0 :(得分:3)

编程时,了解如何创建Minimal, Complete, and Verifiable example (MCVE)非常有用。这使您可以忽略不相关的细节,并专注于问题的核心。

作为一个例子,您的整个代码块可以简化为:

use std::ops::Rem;

fn powm<T>(fbase: &T, modulus: &T)
where
    for<'a> &'a T: Rem<&'a T, Output = T>,
{
    fbase % modulus;
}

fn main() {}

一旦你有了MCVE,你就可以对其进行排列以进行探索。例如,我们可以删除the lifetime elision

fn powm<'a, 'b, T>(fbase: &'a T, modulus: &'b T)
where
    for<'x> &'x T: Rem<&'x T, Output = T>,
{
    fbase % modulus;
}

现在我们开始看到一些事情:这三个生命周期之间的关系是什么?嗯,真的没有一个。如果我们制作一个怎么办?

  • 如果我们说输入引用可以统一为相同 一生,它有效:

    fn powm<'a, T>(fbase: &'a T, modulus: &'a T)
    
  • 如果我们说'b'a更长,那就有效:

    fn powm<'a, 'b: 'a, T>(fbase: &'a T, modulus: &'b T)
    
  • 如果我们说运营商可以有两种不同的生命周期,那就可以了:

    for<'x, 'y> &'x T: Rem<&'y T, Output = T>,
    

如果我们在通话网站上戳一下呢?

  • 如果我们直接调用Rem::rem方法,则可以正常工作:

    Rem::rem(fbase, modulus);
    
  • 如果我们取消引用并重新引用,则可行:

    &*fbase % &*modulus;
    

我不知道完全为什么原来不起作用 - 从概念上讲,输入引用应该能够统一到一个生命周期。可能有一个推论要么不能或不会发生,但我不知道。

与Rust编译器开发人员的进一步讨论导致了an issue,因为它似乎并不正确。这个问题现在已经resolved,理论上应该可以在Rust 1.23中使用。