Черты ржавчины: границы могут быть не реализованы, а черты, которые я реализовал, не существуют - PullRequest
0 голосов
/ 04 сентября 2018

Итак, я пытался реализовать библиотеку для векторной и матричной математики и создал несколько функций, которые работали хорошо, но хотели обобщить для всех числовых примитивов и добавить функциональность в обычные операторы.

Я думал, что я создам контейнер для Vec<T>, который может содержать либо числовые типы (например, i32), либо другой контейнер для Vec, чтобы матрицы, где это возможно. Ergo:

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

Затем, чтобы сложить два вектора любого числа, я использую Добавить как:

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() - это просто функция развёртывания, хотя, вероятно, и с плохим именем.

Базовый сценарий сложения двух векторов любого числа, кажется, работает.

#[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]);
}

Но матрицы вызывают у меня головную боль. Для них функция добавления выглядит очень похоже, за исключением оператора 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 может быть не реализовано для &Mat<T>, но я подумал, что я определил границу, чтобы она соответствовала этому требованию в where T: Add<&'a Mat<T>. Мне кажется, что все, что находится в &a[i], должно иметь реализованную черту Add. Что я здесь не так делаю?

Так же, как дополнительное пояснение, моя идея состоит в том, что Add for &'a Mat<Mat<T>> должен быть в состоянии вызываться рекурсивно, пока он не сводится к Vec с фактическим типом числа в нем. Тогда следует назвать Add for &'a Mat<T>.

1 Ответ

0 голосов
/ 05 сентября 2018

Есть две проблемы: неверно связанный тип 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<Mat<T>>, если вы думаете X как type X = Mat<T>, тогда достаточно impl для Mat<T>:

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

с дополнительным значением для Mat<T> значений:

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

Ниже, я публикую полный рабочий код, обратите внимание, что тип Output больше не является Option<Mat<T>>, а простым Mat<T> объектом: это позволяет избежать многих головных болей и, возможно, это концептуально неправильно, если вы хотите использовать алгебру какого-либо типа.

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<T> не является матрицей в классическом смысле, возможно, другое имя должно быть более подходящим, чтобы избежать путаницы.

...