我試圖理解通用函式。我知道這個話題已經被廣泛討論過,但我似乎無法理解一些細節。
我正在嘗試類似的東西,但更簡單。我只想要一個double將引數乘以 2 的函式,并且我希望它適用于任何浮點或整數型別。到目前為止,我有這個:
use std::convert::From;
use std::ops::Mul;
fn double<A>(x: A) -> A
where A: Mul<Output = A> From<f32> {
return x * A::from(2f32);
}
這很好用,當我用浮點數(f32或f64)呼叫它時:
println!("{:?}", double(2f64));
這給了我4.0.
但它對于任何整數型別都失敗,例如:
printl!("{:?}", double(2i32));
這給出了以下錯誤:
println!("{:?}", double(2i32));
------ ^^^^ the trait `From<f32>` is not implemented for `i32`
我理解錯誤。沒有將浮點數轉換為整數的通用方法。如果我想2.5在我的函式中而不是2.0.
我知道有這個TryFrom特性,但我不知道在這種情況下如何利用它。
我嘗試了另一種方式,即例如需要A實作From<u32>,但這只是將錯誤從整數發生切換到浮點數發生。但令我驚訝的是,當我使用帶符號整數(如2i32.
我錯過了什么?如何double在任何數字型別上實作通用函式?
uj5u.com熱心網友回復:
由于您只需要將數字轉換2為任何型別,您可以從可能的最小型別轉換,即From<u8>:
use std::ops::Mul;
fn double<A>(x: A) -> A
where
A: Mul<Output = A> From<u8>,
{
return x * A::from(2);
}
fn main() {
println!("{}", double(1));
println!("{}", double(1f32));
println!("{}", double(1f64));
}
操場
這種簡單方法的唯一缺點是double(1i8)無法編譯(但可以與i16,i32等一起使用)。From<u8>沒有實作,i8因為有些u8值不能表示為i8. 但是,我們知道我們確實不需要將所有u8值表示為A,而只需 2u8。這就是為什么我們要使用TryFrom:
fn double<A>(x: A) -> A
where
A: Mul<Output = A> TryFrom<u8>,
{
x * A::try_from(2).unwrap_or_else(|_| unreachable!())
}
unwrap 永遠不會為數字型別恐慌,因為沒有不能代表數字 2 的數字型別。
在這種情況下,您無需擔心解包會使代碼變慢,因為 rustc/LLVM 了解 egf64::try_from(2u8)是絕對可靠的并省略了恐慌,正如在 godbolt 上看到的那樣。唯一的缺點是人類讀者可能會感到困惑,這就是為什么使用unreachable!().
uj5u.com熱心網友回復:
From<f32>意味著任何 f32值都可以無損失地轉換為目標型別。這顯然無法完成,i32因為所有非整數值都會在轉換中丟失它們的小數部分。
相反,因為只有 24 位尾數,所以From<u32>無法完成,因此任何大于該值的值都會降低轉換的精度。也不能這樣做,因為大于不能表示的值。f32f322^24From<u32>i322^31
由于您對轉換感興趣的唯一值是2,因此您可以使用From<i8>or From<u8>,但請注意,對于另一個值,它仍然會失敗:
use std::ops::Mul;
fn double<A>(x: A) -> A
where A: Mul<Output = A> From<i8> {
return x * A::from(2);
}
fn main() {
println!("{}", double(1));
println!("{}", double(1f32));
println!("{}", double(1f64));
// println!("{}", double(1u8)); // Doesn't work
}
操場
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/510465.html
標籤:仿制药锈类型
下一篇:兩個類的泛型:.NET6
