rust 如何处理可能出错的自定义类型的数学运算(例如Ok(1)+ Ok(2))

6yoyoihd  于 2024-01-08  发布在  其他
关注(0)|答案(1)|浏览(142)

上下文

我正在努力在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> {
        ...
    }
}

字符串
在写类型的时候感觉很对,但现在数学运算感觉非常僵硬,因为每个操作都需要手动解包。请参见下面的示例,其中匹配的xy变量的类型为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,但收到一个编译器错误,说明FieldElementMul实现有两个冲突。我也在这里做了一些阅读,发现这是预期的,因此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

2fjabf4q

2fjabf4q1#

正如jthulhu在评论中提到的,在有限域中的数学运算通常不会失败,因为你有一个简单的方法来确定两个项目是否在同一个有限域中,所以可以安全地假设想要验证这一事实(从而验证运算是否会成功)的用户可以调用该方法。
如果两个字段不相同,我会做的只是panic。这是一个程序员错误,有一个简单的方法来检查。此外,这反映了Rust中的其他操作,如索引,以同样的方式panic,同时提供了简单的方法来确定如果调用者不确定,它们是否会成功。

相关问题