質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

Q&A

解決済

1回答

3819閲覧

Rustのnum::Integerについて

Paalon

総合スコア232

Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

0グッド

1クリップ

投稿2019/04/03 12:16

このプログラムがエラーになる理由が分かりません。
トレイト?に関して誤解していると思われるのですが理由が分かりません。

rust

1extern crate num; 2 3use num::Integer; 4 5fn test<T: Integer>(n: T) -> T { 6 let m = n + 1; 7 return m; 8} 9 10fn main() { 11 let x: i64 = 10; 12 let y = test(x); 13 println!("{}", y); 14}

また、この例

rust

1extern crate num; 2 3use num::Float; 4 5fn main() { 6 let f1: f32 = 2.0; 7 let f2: f64 = 3.0; 8 9 println!("{:?}", sqrt(f1)); 10 println!("{:?}", sqrt(f2)); 11} 12 13fn sqrt<T: Float>(input: T) -> T { 14 input.sqrt() 15}

が動く理由も分かりません。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

結論

簡潔に原因だけを説明すると、 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 は喜んでご提示のコードをコンパイルします。より具体的には IntegerAdd<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 13:58

編集2019/04/03 14:15
Eki

総合スコア429

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tachikoma

2019/04/03 14:12

継承関係の説明を追いかけていると、2つ目の例にある<t: Integer + One>は<T: Integer>でもいい気がしたのですが、明示的にOneを加えた意図って何かありますでしょうか・・・?
Eki

2019/04/03 14:15 編集

すいません、ありません....。 別の件をコネクリ回しているうちに頭から抜けていたようです。本文のコードの方を修正します。
tachikoma

2019/04/03 14:18

あ、いえただ気になっただけです。説明としてはわかりやすかったので、残っていてもいいのかなとも思いました(^^;
termoshtt

2019/04/04 06:52

変換について細かい補足なのですが、`From<i32>`は`u32`などが対応していないので、同じnum crateの`num::NumCast`を使ってしまうのが良いと思います `fn test<T: Integer + NumCast>(n: T) -> T`
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問