上下文
我正在努力在Rust中实现字段操作,以获得更多关于密码学和Rust的知识。我有一个FieldElement类型,我已经在其上实现了std::ops
的基本特征。
use anyhow::{anyhow, Result};
use std::ops::{Sub, Add, Mul, Div};
use num_traits::PrimInt;
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct FieldElement {
num: i128,
prime: i128
}
impl FieldElement {
pub fn new(num: i128, prime: i128) -> Result<Self> {
if prime <= 0 {
return Err(anyhow!("prime must be non-negative: ({},{})", num, prime));
}
if num >= prime {
return Err(anyhow!("num must be less than prime: ({},{})", num, prime));
}
Ok(Self {
num,
prime
})
}
pub fn pow(&self, exp: i128) -> Result<Self> {
...
}
fn same_field(a: &FieldElement, b: &FieldElement) -> bool {
a.prime == b.prime
}
}
impl Add for FieldElement {
type Output = Result<FieldElement>;
fn add(self, other: Self) -> Result<Self> {
...
}
}
impl Sub for FieldElement {
type Output = Result<FieldElement>;
fn sub(self, other: Self) -> Result<Self> {
...
}
}
impl Mul for FieldElement {
type Output = Result<FieldElement>;
fn mul(self, other: Self) -> Result<Self> {
...
}
}
// Used for Scalar multiplication
impl <T: PrimInt> Mul<T> for FieldElement {
type Output = FieldElement;
fn mul(self, rhs: T) -> Self {
...
}
}
impl Div for FieldElement {
type Output = Result<FieldElement>;
fn div(self, other: Self) -> Result<Self> {
...
}
}
字符串
在写类型的时候感觉很对,但现在数学运算感觉非常僵硬,因为每个操作都需要手动解包。请参见下面的示例,其中匹配的x
和y
变量的类型为FieldElement
:
...
impl CurvePoint {
pub fn new(point: Option<Point>, a: FieldElement, b: FieldElement) -> Result<Self> {
if let Some(Point {x, y}) = point {
let rhs = ((x.pow(3)? + (a * x)?)? + b)?;
if y.pow(2)? != rhs {
return Err(anyhow!("(x,y) must be on the curve x^3 + ax + b"));
}
}
Ok(Self { point, a, b })
}
}
型
虽然这是正确的,但括号和问号会给业务逻辑增加干扰。
问题
这里有更好的做法吗?我想避免恐慌,但在这种情况下也许这是合适的。我也可以实现处理Err
检查的方法,但我试图坚持操作符重载。
尝试次数
Try 1:实现结果
我的第一次尝试是实现Result<FieldElement>
的操作traits。我是Rust的新手,所以这似乎是合理的。然而,我收到了一个编译器错误,说明'Result' is not defined in the current create
。我对此做了一些阅读,似乎是预期的。
尝试2:实现FieldElement的Op
我以前在尝试实现标量乘法时遇到过类似的错误消息(1)。我尝试impl Mul<Result<FieldElement>> for FieldElement
,但收到一个编译器错误,说明FieldElement
的Mul
实现有两个冲突。我也在这里做了一些阅读,发现这是预期的,因此trait不能实现两次(尽管这正是我希望做的)。以下是相互矛盾的特征:
impl <T: PrimInt> Mul<T> for FieldElement { ... }
impl Mul<Result<FieldElement>> for FieldElement { ... }
型PrimInt
trait绑定来自num_traits
。它是我的标量乘法实现(例如3 * FieldElement{1,13})的一部分,我需要支持它。
试试3:宏?
在这一点上,我超出了我的深度,所以我开始四处寻找。我发现了auto_ops库,但它似乎专注于所有权的操作变体。虽然这不能解决我的问题,但我想我可能想采取类似的方法。
尝试4:调整标量乘法后尝试2
由于我知道我正在处理的整数类型(i128
),如果我调整标量乘法特征,我可以遵循2中描述的方法:
impl Mul for FieldElement {
type Output = Result<FieldElement>;
fn mul(self, other: Self) -> Result<Self> {
...
}
}
impl Mul<Result<FieldElement>> for FieldElement {
type Output = Result<FieldElement>;
fn mul(self, rhs: Result<FieldElement>) -> Self::Output {
if let Ok(el) = rhs {
el * self
} else {
rhs
}
}
}
impl Mul<FieldElement> for i128 {
type Output = FieldElement;
fn mul(self, rhs: FieldElement) -> Self::Output {
...
}
}
型
在其他操作中采用这种方法,我可以写remove一些语法,但我认为由于非标准形式,可读性更差。它也没有完全解决我的问题。
// Before
((x.pow(3)? + (a * x)?)? + b)?;
// After
(b + a*x)? + x.pow(3);
// Ideal
let foo: Result<FieldElement> = x.pow(3) + a*x + b
型
1条答案
按热度按时间2fjabf4q1#
正如jthulhu在评论中提到的,在有限域中的数学运算通常不会失败,因为你有一个简单的方法来确定两个项目是否在同一个有限域中,所以可以安全地假设想要验证这一事实(从而验证运算是否会成功)的用户可以调用该方法。
如果两个字段不相同,我会做的只是panic。这是一个程序员错误,有一个简单的方法来检查。此外,这反映了Rust中的其他操作,如索引,以同样的方式panic,同时提供了简单的方法来确定如果调用者不确定,它们是否会成功。