【问题标题】:Rust traits: The bounds might not be implemented, and the traits I've implemented does not existRust 特征:边界可能没有实现,我实现的特征不存在
【发布时间】:2018-09-04 09:42:32
【问题描述】:

所以我一直在尝试实现一个向量和矩阵数学库,并且我创建了一些可以正常工作但想要概括所有数字基元并将功能添加到普通运算符的函数。

我的想法是为Vec<T> 创建一个容器,该容器可以包含数字类型(如i32)或Vec 的另一个容器,以便尽可能使用矩阵。尔格:

#[derive(Clone, Debug)]
struct Mat<T>(Vec<T>);

然后,将两个任意数量的 vecs 相加,我实现 Add as:

impl<'a, T> Add for &'a Mat<T>
where T: PartialEq + PartialOrd + Add<T> + Sub<T> + Mul<T> + Div<T> + Rem<T> + Clone {
    type Output = Option<Mat<<T as std::ops::Add>::Output>>;

    fn add(self, other: &Mat<T>) -> Self::Output {
        let a: &Vec<T> = self.pop();
        let b: &Vec<T> = other.pop();
        match a.len() == b.len() {
            true => {
                let mut retvec: Vec<<T as std::ops::Add>::Output> = Vec::new();
                for i in 0..a.len() {
                    retvec.push(a[i].clone() + b[i].clone());
                }
                Some(Mat(retvec))
            },
            false => None
        }
    }
}

编辑:为了进一步澄清,Mat::pop() 只是 unwrap 函数,尽管名称可能很糟糕。

将任意数量的两个向量相加的基本方案似乎可行。

#[test]
fn add_override_vectors() {
    let vec: Mat<i32> = Mat(vec![2, 2, 2]);
    let newvec = &vec + &vec;

    assert_eq!(*newvec.unwrap().pop(), vec![4,4,4]);
}

但是矩阵让我很头疼。对他们来说,add 函数看起来非常相似,除了 let Some(x) 语句:

impl<'a, T> Add for &'a Mat<Mat<T>>
where T: Add<&'a Mat<T>>{
    type Output = Option<Mat<T>>;

    fn add(self, other: &Mat<Mat<T>>) -> Self::Output {
        let a: &Vec<Mat<T>> = self.pop();
        let b: &Vec<Mat<T>> = other.pop();
        match a.len() == b.len() {
            true => {
                let mut retvec: Vec<T> = Vec::new();
                for i in 0..a.len() {
                    if let Some(x) = &a[i] + &b[i] {
                        retvec.push(x);
                    }
                }
                Some(Mat(retvec))
            },
            false => None
        }
    }
}

我得到的错误信息是:

error[E0369]: binary operation `+` cannot be applied to type `&Mat<T>`
  --> src\main.rs:46:38
   |
46 |                     if let Some(x) = &a[i] + &b[i] {
   |                                      ^^^^^^^^^^^^^
   |
   = note: an implementation of `std::ops::Add` might be missing for `&Mat<T>`

所以编译器说Add 可能不会为&amp;Mat&lt;T&gt; 实现,但我认为我已经指定了边界,以便它在where T: Add&lt;&amp;'a Mat&lt;T&gt; 中具有该要求。在我看来,&amp;a[i] 中的任何内容似乎都应该实现 Add trait。我在这里做错了什么?

作为额外的澄清,我的想法是Add for &amp;'a Mat&lt;Mat&lt;T&gt;&gt; 应该能够被递归调用,直到它归结为Vec,其中包含一个实际的数字类型。然后应该调用Add for &amp;'a Mat&lt;T&gt;

【问题讨论】:

  • 嗯,我想我看错了。您的意思是在实现内部的某处将T 添加到Mat&lt;T&gt; 吗?这就是T: for&lt;'b&gt; Add&lt;&amp;'b Mat&lt;T&gt;&gt; 所暗示的。我怀疑你实际上想要for&lt;'b&gt; &amp;'b Mat&lt;T&gt;: Add(但这会导致更多问题)
  • 我不确定我是否得到了您的要求,如果您的意思是我忘记为 TMat&lt;T&gt; 实现 Add 函数,那么我认为我不会需要一个,因为 forloop 循环遍历两个函数中的两个向量,它只需要添加T + TMat&lt;T&gt; + Mat&lt;T&gt;,对吗?否则,如果你的意思是如果我尝试做T + Mat&lt;T&gt;,那么不,我发布的测试是我迄今为止唯一的测试。
  • 您的where 子句适用于T + &amp;Mat&lt;T&gt;,但您在函数内部实际执行的是&amp;Mat&lt;T&gt; + &amp;Mat&lt;T&gt;(我认为)。但这还不是全部,因为修复绑定会导致其他问题。
  • 我认为,你想要的是&amp;a[i] + &amp;b[i],而不是a[i].clone() + b[i].clone()

标签: matrix vector rust traits bounds


【解决方案1】:

有两个问题:错误关联Output类型和retvec的类型

类似的东西应该可以工作:

impl<'a, T> Add for &'a Mat<Mat<T>>
where
    T: PartialEq + PartialOrd + Add<T> + Clone,
{
    type Output = Option<Mat<Mat<<T as std::ops::Add>::Output>>>;

    fn add(self, other: &Mat<Mat<T>>) -> Self::Output {
        let a: &Vec<Mat<T>> = self.pop();
        let b: &Vec<Mat<T>> = other.pop();
        match a.len() == b.len() {
            true => {
                let mut retvec: Vec<Mat<<T as std::ops::Add>::Output>> = Vec::new();
                for i in 0..a.len() {
                    if let Some(x) = &a[i] + &b[i] {
                        retvec.push(x);
                    }
                }
                Some(Mat(retvec))
            }
            false => None,
        }
    }
}

编译问题的一部分,我认为为“递归”结构实现特征是不正确的 比如Mat&lt;Mat&lt;T&gt;&gt;,如果你认为Xtype X = Mat&lt;T&gt;,那么Mat&lt;T&gt; 的impl 就足够了:

impl<'a, T> Add for &'a Mat<T>
where
    T: PartialEq + PartialOrd + Add<T> + Clone

带有Mat&lt;T&gt; 值的附加实现:

impl<T> Add for Mat<T>
where
    T: PartialEq + PartialOrd + Add<T> + Clone

下面我发布了一个完整的工作代码,请注意Output 类型不再是Option&lt;Mat&lt;T&gt;&gt;,而是一个普通的Mat&lt;T&gt; 对象: 这避免了很多麻烦,如果你想实现某种类型的代数,这可能在概念上是错误的。

use std::ops::*;
use std::vec::Vec;

#[derive(Clone, Debug, PartialEq, PartialOrd)]
struct Mat<T>(Vec<T>);

impl<T> Mat<T> {
    fn pop(&self) -> &Vec<T> {
        &self.0
    }
}

impl<T> Add for Mat<T>
where
    T: PartialEq + PartialOrd + Add<T> + Clone,
{
    type Output = Mat<<T as std::ops::Add>::Output>;

    fn add(self, other: Mat<T>) -> Self::Output {
        let a: &Vec<T> = self.pop();
        let b: &Vec<T> = other.pop();
        match a.len() == b.len() {
            true => {
                let mut retvec: Vec<<T as std::ops::Add>::Output> = Vec::new();
                for i in 0..a.len() {
                    retvec.push(a[i].clone() + b[i].clone());
                }
                Mat(retvec)
            }
            false => Mat(Vec::new()),
        }
    }
}

impl<'a, T> Add for &'a Mat<T>
where
    T: PartialEq + PartialOrd + Add<T> + Clone,
{
    type Output = Mat<<T as std::ops::Add>::Output>;

    fn add(self, other: &Mat<T>) -> Self::Output {
        let a: &Vec<T> = self.pop();
        let b: &Vec<T> = other.pop();
        match a.len() == b.len() {
            true => {
                let mut retvec: Vec<<T as std::ops::Add>::Output> = Vec::new();
                for i in 0..a.len() {
                    retvec.push(a[i].clone() + b[i].clone());
                }
                Mat(retvec)
            }
            false => Mat(Vec::new()),
        }
    }
}


#[test]
fn add_override_vectors() {
    let vec: Mat<Mat<i32>> = Mat(vec![Mat(vec![2, 2, 2]), Mat(vec![3, 3, 3])]);
    let newvec = &vec + &vec;

    assert_eq!(*newvec.pop(), vec![Mat(vec![4, 4, 4]), Mat(vec![6, 6, 6])]);
}

#[test]
fn add_wrong_vectors() {
    let vec1: Mat<Mat<i32>> = Mat(vec![Mat(vec![2, 2, 2]), Mat(vec![4, 4, 4])]);
    let vec2: Mat<Mat<i32>> = Mat(vec![Mat(vec![3, 3, 3]), Mat(vec![3, 3])]);
    let newvec = &vec1 + &vec2;

    assert_eq!(*newvec.pop(), vec![Mat(vec![5, 5, 5]), Mat(vec![])]);
}


fn main() {
    let vec: Mat<Mat<i32>> = Mat(vec![Mat(vec![1, 2, 2]), Mat(vec![3, 3, 3])]);
    let newvec = &vec + &vec;

    println!("Hello, world!: {:?}", newvec);
}

PS:您的Mat&lt;T&gt; 类型不是经典意义上的矩阵,也许另一个名称应该更合适以避免混淆。

【讨论】:

  • 编辑:不,愚蠢的我。忘记派生 PartialEq 和 PartialOrd。正如我所说,非常感谢!顺便说一句,这不是矩阵吗?我已经将它们描述为向量的向量。谢谢大佬,很好的解释!是的,我同意Mat&lt;Mat&lt;T&gt;&gt; 在概念上很弱,但那是我在与编译器作斗争。即使现在我已经复制了您的代码,如果我将 Mat&lt;Mat&lt;T&gt;&gt; 测试留在其中,它也不会编译。仍然是同样的错误:“Mat&lt;Mat&lt;i32&gt;&gt; 可能缺少 std::ops::Add 的实现”,并且您说它有效因为你让我怀疑存在的逻辑和理由。
  • 通常矩阵被定义为多维数组,每个维都有相同的基数。使用您的 Mat&lt;Mat&lt;T&gt;&gt; 类型,您可以拥有不同大小的行。
  • 对,但它实际上无法计算那些格式错误的矩阵。你会建议什么来解决这个问题,一个宏或者一个 Mat::new() 成员函数,使它符合正确的向量维度?
猜你喜欢
  • 1970-01-01
  • 2023-03-29
  • 1970-01-01
  • 2019-12-08
  • 2021-07-18
  • 2016-05-29
  • 2022-12-07
  • 2019-12-17
  • 2022-10-07
相关资源
最近更新 更多