2015-06-19 137 views
6

我有一個關於泛型在Rust(1.0版)的新手問題。鑄造到一個通用的類型

假設我寫了一個通用函數來進行除法。沒關係這種功能的用處;讓這個問題簡單化是一個簡單的功能。

fn divide<T: std::ops::Div>(a: T, b: T) -> T { 
    a/b 
} 

fn main() { 
    println!("{}", divide(42, 18)) 
} 

該程序未能編譯。

src/main.rs:2:5: 2:10 error: mismatched types: 
expected `T`, 
    found `<T as core::ops::Div>::Output` 
(expected type parameter, 
    found associated type) [E0308] 
src/main.rs:2  a/b 
        ^~~~~ 

我明白,編譯器錯誤是告訴我,除法運算的結果是一個類型Output,不T,我看到standard library documentationOutput類型。

如何將Output轉換爲T?我嘗試使用as進行施放。

fn divide<T: std::ops::Div>(a: T, b: T) -> T { 
    (a/b) as T 
} 

fn main() { 
    println!("{}", divide(42, 18)) 
} 

這會導致不同的編譯器錯誤。

src/main.rs:2:5: 2:17 error: non-scalar cast: `<T as core::ops::Div>::Output` as `T` 
src/main.rs:2  (a/b) as T 
        ^~~~~~~~~~~~ 

我的想法,使這項工作,我意識到我缺乏的關於語言的東西基本瞭解這裏,但我甚至不知道要尋找什麼,使這項工作。幫幫我?

+1

可能是http://stackoverflow.com/q/29184358/155423的副本。 – Shepmaster

回答

9

您只需指定T::Output作爲返回類型的函數:

fn divide<T: std::ops::Div>(a: T, b: T) -> T::Output { 
    a/b 
} 

編輯以添加,爲什麼你當你在不能這樣做的功能

裏面投更多的解釋你的泛型函數鴻溝,編譯器還不知道你可以投TT::Output,所以演員陣容是無效的。它們是通用類型,它們可以是任何東西,編譯器知道如何從T轉換爲T::Output

a/b生成T::Output類型的東西,所以在上面的解決方案中沒有投射,T::Output只是正確的類型。

編輯添加使用std另一種可能的解決方案::轉換::從

最(我認爲)通用的實現是當你知道從T::Output投給T是可能的。對於T::Output,您可以綁定T以實施From。 這是一個完整的例子:

use std::ops::Div; 
use std::convert::From; 

fn divide<T: Div>(a: T, b: T) -> T 
    where T: From<<T as Div>::Output> 
{ 
    T::from(a/b) 
} 

#[derive(Debug)] 
struct Bip(u32); 

impl Div for Bip { 
    type Output = f32; 

    fn div(self, rhs: Bip) -> f32 { 
     (self.0/rhs.0) as f32 
    } 
} 

impl From<f32> for Bip { 
    fn from(value: f32) -> Self { 
     Bip(value as u32) 
    } 
} 

fn main() { 
    println!("{:?}", divide(12, 4)); 
    println!("{:?}", divide(Bip(12), Bip(4))); 
} 
+0

這有效,但_why_它工作嗎?如果我用類型爲'u32'的'T'調用'divide',並將返回的值賦給一個'u32'類型的變量(即'let x:u32 = divide(42u32,18u32);'),然後編譯。爲什麼它在'divide - > T'不能從'T :: Output'轉換爲'T'的時候返回表達式,但是當在divide之外使用返回值時,類型系統允許轉換(從'u32 :: Output '到'u32')?即,將''Output'追加到'divide'的返回類型將函數_inside_轉換爲_outside_函數。這裏發生了什麼? –

+2

因爲當你在你的通用函數'divide'中時,編譯器還不知道你可以''T「輸出'T:Output'。 (它們是通用的,它們可以是任何東西,甚至是不能轉換的東西)'a/b'產生'T :: Output'類型的東西,所以在上面的解決方案中不存在轉換,'T :: Output'只是簡單的正確的類型。 – eulerdisk

4

Andreas的回答解釋了爲什麼劇組是不可能的,這使得功能儘可能通用的解決了這個問題。

然而,這不是唯一的解決方案。 Rust還支持限制泛型函數的關聯類型(此處爲Output)。

另一種方法是:

use std::ops::Div; 

fn divide<T>(a: T, b: T) -> T 
    where T: Div<Output = T> 
{ 
    a/b 
} 

<Output = T>位指示編譯器只接受在此功能類型T對於其Div執行具有Output等於T。這顯然更受限制,但它可以確保divide的結果爲T

+2

'='約束的好處。在官方文檔中我沒有發現太多,你能給我們一些參考嗎?但是,如果要添加更多內容,則可以要求''''''''''''''''''''''''''''可以實現'T'。 (我再次編輯我的回覆) – eulerdisk

+1

@AndreaP:它被暗指在[相關類型]的最後(https://doc.rust-lang.org/book/associated-types.html#trait-objects-with相關類型)章節。關於他們沒什麼可說的,所以文件不多說,但我承認很容易錯過。 –