2016-11-13 7 views
0

Я хочу реализовать черту для структуры общего типа. Метод внутри блока признаков должен возвращать не общий тип. У меня возникают проблемы при попытке бросить:Нескалярный приведение: `T` как` f64` при выполнении общего умножения

struct Rectangle<T> { 
    x: T, 
    y: T, 
    width: T, 
    height: T, 
} 

trait Area { 
    fn area(&self) -> f64; 
} 

impl<T> Area for Rectangle<T> 
    where T: std::ops::Mul<Output=T> 
{ 
    fn area(&self) -> f64 { 
     let t_area = self.height * self.width; 
     let f_area = t_area as f64; 
     f_area 
    } 
} 

fn main() { 
    let sq = Rectangle { x: 0, y: 0, height: 1, width: 1 }; 
    println!("{}", sq.area()); 
} 

И выход компилятор:

error: non-scalar cast: `T` as `f64` 
    --> src/main.rs:22:22 
    | 
22 |   let f_area = t_area as f64; 
    |      ^^^^^^^^^^^^^ 

Могу ли я бросить T к f64 без использования небезопасных блоков?

ответ

3

Могу ли я, не используя небезопасные блоки, отлитые T - f64?

Что такое T? Код для преобразования u8 в f64, безусловно, не совпадает с кодом для преобразования u64 в f64 (последний может завершиться неудачей, в конце концов).

Как я понимаю, у вас есть два пути впереди вас:

  • сделать Area родовое над T, и, таким образом, вернуть T
  • Ограничить T для типов, которые могут быть преобразованы в f64

Я продемонстрирую последний:

impl<T> Area for Rectangle<T> 
    where T: std::ops::Mul<Output=T> + Clone + std::convert::Into<f64> 
{ 
    fn area(&self) -> f64 { 
     self.height.clone().into() * self.width.clone().into() 
    } 
} 
+0

Следует отметить, что 'Into' просто представляет преобразования в подтип исходного типа (те, которые никогда не могут потерять точность). Но я бы сказал, что в этом случае программисту, вероятно, все равно, если области 'u64' или' i64' будут отбрасываться на 'f64' с возможной потерю точности ... Возможно, они это делают. –

+0

@ LukasKalbertodt: Я не знаю точно. Если бы ['TryInto'] (https://doc.rust-lang.org/std/convert/trait.TryInto.html) был стабильным, я бы посоветовал это. –

3

Могу ли я, не используя небезопасные блоки, отлитые T - f64?

Нет, и есть много веских причин для этого. T может быть любого типа, поэтому он не должен быть совместим с f64вообще. Единственное, что вы знаете о своем T, это то, что оно реализует std::ops::Mul<Output=T>. Но это не помогает, потому что эта черта ничего не говорит о f64.

Так что вы могли do, обязаны T быть std::ops::Mul<Output=f64>. Это означает, что умножение двух T s приведет к f64. Затем ваша функция работает (даже без приведения as f64), но вы делаете свой путь менее универсальным.


Правильный способ решения этой проблемы - отвлечься от нескольких видов чисел. К счастью, num-traits crate уже сделал это. В частности, вас, вероятно, интересует ToPrimitive trait. Ваш осущ должен тогда, вероятно, выглядеть примерно так:

use num_traits::ToPrimitive; 

impl<T> Area for Rectangle<T> 
    where T: std::ops::Mul 
      <T as std::ops::Mul>::Output: ToPrimitive 

{ 
    fn area(&self) -> f64 { 
     let t_area = self.height * self.width; 
     t_area.to_f64().unwrap() 
    } 
} 

Незначительные проблемы: мы имеем эту unwrap() здесь, который паникует, когда результат умножения не может быть преобразован в f64. Я не совсем понимаю, почему это может произойти, но мы должны знать об этом. Возможно, есть лучшее решение без такого unwrap().


Вы также можете создать свою собственную черту. Что-то вроде:

trait AsF64 { 
    fn cast(self) -> f64; 
} 

impl AsF64 for i32 { 
    fn cast(self) -> f64 { self as f64 } 
} 

// more impls for u32, u16, i16, ... 

А потом осущ для <T as std::ops::Mul>::Output: AsF64.