Время жизни ржавчины для параметров типа - PullRequest
0 голосов
/ 04 ноября 2018

Я работаю над примером простого комплексного числа и пытаюсь реализовать операции ref-value / value-ref следующим образом:

use std::ops::*;

#[derive(Clone, PartialEq)]
pub struct Complex<T: Sized + Clone> {
    pub re: T,
    pub im: T,
}

// Ref-Ref Multiplication
impl<'a, 'b, T: Sized + Clone> Mul<&'b Complex<T>> for &'a Complex<T>
where
    T: Add<T, Output = T>,
    T: Sub<T, Output = T>,
    &'a T: Add<&'b T, Output = T>,
    &'a T: Mul<&'b T, Output = T>,
    &'a T: Sub<&'b T, Output = T>,
{
    type Output = Complex<T>;
    fn mul(self, rhs: &'b Complex<T>) -> Complex<T> {
        panic!("// Details irrelevant")
    }
}

// Ref-Value Multiplication
impl<'a, 'b, T: Sized + Clone> Mul<Complex<T>> for &'a Complex<T>
where
    T: 'static,
    T: Add<T, Output = T>,
    T: Sub<T, Output = T>,
    &'a T: Add<&'b T, Output = T>,
    &'a T: Mul<&'b T, Output = T>,
    &'a T: Sub<&'b T, Output = T>,
{
    type Output = Complex<T>;
    fn mul(self, rhs: Complex<T>) -> Complex<T> {
        let t = &rhs;
        self.mul(t)
    }
}

Реализация ref-ref работает, и, насколько я понимаю, она берет две ссылки разного времени жизни и возвращает сложный тип значения. В части ref-value у меня возникла проблема; Когда я компилирую, ошибка в том, что rhs не живет достаточно долго. Мне кажется, я знаю, почему это уже так, и что T может содержать ссылку (прямую или косвенную) на rhs при возврате значения, таким образом, rhs выходит из области видимости, но T может содержать ссылку на это все еще.

Мой вопрос заключается в том, как сообщить, что T не будет содержать некоторую ссылку на rhs в той или иной форме.

Некоторые заметки о вещах, которые я до сих пор пробовал или смотрел:

  1. Изменена спецификация времени жизни в любой реализации Mul.
  2. Пробное наследование жизни, но при этом указывается, что ссылка, хранящаяся в T, будет жить по крайней мере столько же, сколько T, поэтому я думаю, что мне нужно что-то большее в строках "максимум".
  3. Посмотрел другие реализации; Либо не реализует дело, либо просто использует клон для обхода проблемы.

Ответы [ 2 ]

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

Встроенные числовые типы реализуют эти перестановки, используя макрос . Делая это вручную, я начну со случая, когда вы умножаете два значения вместо любых ссылок, и убедитесь, что ваша Complex структура равна Copy:

impl<T: Copy> Mul<Complex<T>> for Complex<T>
where
    T: Add<T, Output = T>,
    T: Sub<T, Output = T>,
    T: Mul<T, Output = T>,
{
    type Output = Complex<T>;
    fn mul(self, rhs: Complex<T>) -> Complex<T> {
        unimplemented!()
    }
}

Обратите внимание, что вам не нужны никакие ограничения на &T - вы все равно не можете возвращать ссылки на T, поэтому вам придется скопировать их, поэтому я указал T: Copy.

Остальные реализации теперь просты и могут делегироваться в простейшем случае:

impl<'a, T: Copy> Mul<Complex<T>> for &'a Complex<T>
where
    T: Add<T, Output = T>,
    T: Sub<T, Output = T>,
    T: Mul<T, Output = T>,
{
    type Output = Complex<T>;
    fn mul(self, rhs: Complex<T>) -> Complex<T> {
        (*self).mul(rhs)
    }
}

impl<'a, T: Copy> Mul<&'a Complex<T>> for Complex<T>
where
    T: Add<T, Output = T>,
    T: Sub<T, Output = T>,
    T: Mul<T, Output = T>,
{
    type Output = Complex<T>;
    fn mul(self, rhs: &'a Complex<T>) -> Complex<T> {
        self.mul(*rhs)
    }
}

impl<'a, 'b, T: Copy> Mul<&'a Complex<T>> for &'b Complex<T>
where
    T: Add<T, Output = T>,
    T: Sub<T, Output = T>,
    T: Mul<T, Output = T>,
{
    type Output = Complex<T>;
    fn mul(self, rhs: &'a Complex<T>) -> Complex<T> {
        (*self).mul(*rhs)
    }
}
0 голосов
/ 04 ноября 2018

Как предложил Питер Холл в комментариях, самое простое решение - вывести Copy для вашего сложного типа и реализовать операции для значений. Для реализаций ref-ref и реализаций ref-val вы можете просто разыменовать ссылки и использовать реализацию val-val.

Если вы хотите использовать подход, с которого вы начали работать, вам нужны границы черт более высокого ранга:

use std::ops::*;

#[derive(Clone, PartialEq)]
pub struct Complex<T: Clone> {
    pub re: T,
    pub im: T,
}

// Ref-Ref Multiplication
impl<'a, 'b, T: Clone> Mul<&'b Complex<T>> for &'a Complex<T>
where
    T: Add<T, Output = T>,
    T: Sub<T, Output = T>,
    &'a T: Add<&'b T, Output = T>,
    &'a T: Mul<&'b T, Output = T>,
    &'a T: Sub<&'b T, Output = T>,
{
    type Output = Complex<T>;
    fn mul(self, rhs: &'b Complex<T>) -> Complex<T> {
        Complex {
            re: &self.re * &rhs.re - &self.im * &rhs.im,
            im: &self.re * &rhs.im + &self.im * &rhs.re,
        }
    }
}

// Ref-Value Multiplication
impl<'a, T: Clone> Mul<Complex<T>> for &'a Complex<T>
where
    T: Add<T, Output = T>,
    T: Sub<T, Output = T>,
    &'a T: for<'b> Add<&'b T, Output = T>,
    &'a T: for<'b> Mul<&'b T, Output = T>,
    &'a T: for<'b> Sub<&'b T, Output = T>,
{
    type Output = Complex<T>;
    fn mul(self, rhs: Complex<T>) -> Complex<T> {
        let t = &rhs;
        self.mul(t)
    }
}

В вашей версии время жизни 'b в реализации ref-value выбирается пользователем черты. Поскольку пользователь может использовать любое время жизни для 'b, rhs потребуется статическое время жизни для того, чтобы ваш код был действительным. Вместо этого вы хотите, чтобы *'a T удовлетворял заданным границам черты для любого данного времени жизни 'b, что и является тем, для чего предназначены HRTB.

Альтернативный, менее повторяющийся способ написания границ признаков для второй реализации:

impl<'a, T: Clone> Mul<Complex<T>> for &'a Complex<T>
where
    Self: for<'b> Mul<&'b Complex<T>, Output = Complex<T>>,
{
    type Output = Complex<T>;
    fn mul(self, rhs: Complex<T>) -> Complex<T> {
        self.mul(&rhs)
    }
}
...