这与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
的元素。我需要修改我的特质界限吗?为什么会出错?
答案 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中使用。