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

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

ただいまの
回答率

88.78%

Rustのnum::Integerについて

解決済

回答 1

投稿

  • 評価
  • クリップ 1
  • VIEW 1,278

Paalon

score 193

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

extern crate num;

use num::Integer;

fn test<T: Integer>(n: T) -> T {
    let m = n + 1;
    return m;
}

fn main() {
    let x: i64 = 10;
    let y = test(x);
    println!("{}", y);
}

また、この例

extern crate num;

use num::Float;

fn main() {
    let f1: f32 = 2.0;
    let f2: f64 = 3.0;

    println!("{:?}", sqrt(f1));
    println!("{:?}", sqrt(f2));
}

fn sqrt<T: Float>(input: T) -> T {
    input.sqrt()
}

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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+5

結論

簡潔に原因だけを説明すると、 n が型 T であるのに対して 1 は整数であるから、 n + 1 が実行できるかどうか分からない、ということになります。解決する方法としては二択あり、一つは 1 を型 T に変換してしまう方法、もう一つは「T 型で 1 にあたる値」は One::one() によって生成できるのでそれを使うという方法です。後者はかなり状況限られてしまいますが...。

1 つ目は例えば次のようになります。

fn test<T: Integer + From<i32>>(n: T) -> T {
    let m = n + T::from(1);
    return m;
}

2 つ目は例えば次のようになります。

fn test<T: Integer + One>(n: T) -> T {
    let m = n + T::one();
    return m;
}

説明

コードでは実際には型 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 23:12

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

    キャンセル

  • 2019/04/03 23:14 編集

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

    キャンセル

  • 2019/04/03 23:18

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

    キャンセル

  • 2019/04/04 15:52

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

    キャンセル

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

  • ただいまの回答率 88.78%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る