結論
簡潔に原因だけを説明すると、 n
が型 T
であるのに対して 1
は整数であるから、 n + 1
が実行できるかどうか分からない、ということになります。解決する方法としては二択あり、一つは 1
を型 T
に変換してしまう方法、もう一つは「T
型で 1
にあたる値」は One::one()
によって生成できるのでそれを使うという方法です。後者はかなり状況限られてしまいますが...。
1 つ目は例えば次のようになります。
rust
1fn test<T: Integer + From<i32>>(n: T) -> T {
2 let m = n + T::from(1);
3 return m;
4}
2 つ目は例えば次のようになります。
rust
1fn test<T: Integer>(n: T) -> T {
2 let m = n + T::one();
3 return m;
4}
説明
コードでは実際には型 i64
の値を渡しているため T == i64
となります、当然 n + 1
は 今回は 実行できます。しかし、 Rust のジェネリクスは「実際にどのように呼び出されたか」ではなく「T
が型制約 (今なら T: Integer
) を満たす限りどのような型でもうまくいく」ようになっている必要があります。これはつまり、後になって新たに作られた IntegerLike
のような型があったとしても、トレイト Integer
を実装してさえいれば、それがどのような型であっても関数 test()
の引数として渡すことができて、エラーも起こらないということを表します。
そこで、逆に言うと test()
の中では T
は未だ存在しないものを含めてあらゆる Integer
を実装した型を想定しなければなりません。 T
についてできることはトレイト Integer
に定義されていることだけです。
もしここで Integer
に、「Integer
な型は、 T
と整数を足して T
型を生成することができる」という定義があるならば、 Rust は喜んでご提示のコードをコンパイルします。より具体的には Integer
が Add<i32, Output=T>
か何かのサブトレイトであればよいです。
ここでドキュメントを見ると、Integer
のトレイト継承関係は次のようになっているようです (https://docs.rs/num/0.2.0/num/trait.Integer.html で Show declaration を押して開くと辿れます):
Integer
- Eq
- Ord
- PartialOrd<Self>
- Num
- Zero
- One
- NumOps<Self, Self>
- Add<Self, Output=Self> --- 足し算
+
- Sub<Self, Output=Self> --- 引き算
-
- Mul<Self, Output=Self> --- かけ算
*
- Div<Self, Output=Self> --- 割り算
/
- Rem<Self, Output=Self> --- あまり
%
- PartialEq
これを見ると Integer
として要求されている四則演算は「自分と同じ型同士の計算ができて結果が自分と同じ型になる」ということが分かります。 IntegerLike
同士で足し算ができればよく、整数との足し算はできなくても Integer
の定義としては問題ありません。であるなら、 T
として整数との足し算ができない型がくる可能性も考慮する必要があるということなのです。
下のコードですが、 sqrt()
関数は T: Float
であるような T
を受け取って T
を返す関数となっています。なので実装は Float
を実装するようなどのような T
に対しても有効であるようにすれば問題ありません。中身は input.sqrt()
のみですが、ここで Float
トレイトをドキュメントで調べると sqrt()
というメソッドがあり、まさに自分自身をとって自分自身と同じ型を返すものであることが分かります。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/04/03 14:12
2019/04/03 14:15 編集
2019/04/03 14:18
2019/04/04 06:52