引数に基づいてRust関数またはトレイトメソッドの戻り値の型を計算することは可能ですか?

ワイプ

boost::math::tools::promote_argsRustと同様のことを達成できますか?慣用的なC ++ 11タイプのプロモーションも参照してください。

具体的には、引数に基づいて関数またはトレイトメソッドの戻り値の型を計算し、戻り値の型が引数の1つと同じ型であることを確認することは可能ですか?

次の場合を考えてみましょう。私には2つの構造体があります:

#[derive(Debug, Clone, Copy)]
struct MySimpleType(f64);

#[derive(Debug, Clone, Copy)]
struct MyComplexType(f64, f64);

特性MySimpleTypeMyComplexType介してどこに昇格することができますFrom

impl From<MySimpleType> for MyComplexType {
    fn from(src: MySimpleType) -> MyComplexType {
        let MySimpleType(x1) = src;
        MyComplexType(x1, 0.0)
    }
 }

私はタイプの二つの引数を取る関数書きたいMySimpleTypeかをMyComplexTypeし、型の値を返すMySimpleTypeすべての引数がのように入力された場合MySimpleType、それ以外の機能は、型の値を返す必要がありますMyComplexTypeAdd<Output=Self>両方のタイプに実装したとすると、次のようなことができます。

trait Foo<S, T> {
    fn foo(s: S, t: T) -> Self;
}

impl<S, T, O> Foo<S, T> for O
    where O: From<S> + From<T> + Add<Output = Self>
{
    fn foo(s: S, t: T) -> Self {
        let s: O = From::from(s);
        let t: O = From::from(t);
        s + t
    }
}

しかし、その後、コンパイラはそれが分かっていないOのいずれかであるべきSか、Tと私はほとんどのメソッド呼び出しに注釈を付ける必要があります。

私の2番目の試みは、わずかに異なる特性を使用して、2つの実装を作成することです。

trait Foo<S, T> {
    fn foo(s: S, t: T) -> Self;
}

impl Foo<MySimpleType, MySimpleType> for MySimpleType {
    fn foo(s: MySimpleType, t: MySimpleType) -> Self {
        s + t
    }
}

impl<S, T> Foo<S, T> for MyComplexType
    where MyComplexType: From<S> + From<T>
{
    fn foo(s: S, t: T) -> Self {
        let s: MyComplexType = From::from(s);
        let t: MyComplexType = From::from(t);
        s + t
    }
}

しかし、繰り返しになりますが、コンパイラはの戻り値の型を把握できません。

Foo::foo(MySimpleType(1.0), MySimpleType(1.0))

3番目の試行は、に似たものstd::ops::{Add, Mul, ...}です。関連する型を使用し、引数型の可能な組み合わせごとに特定の実装を記述します

trait Foo<T> {
    type Output;
    fn foo(self, t: T) -> Self::Output;
}

impl<T: Add<Output=T>> Foo<T> for T {
    type Output = Self;
    fn foo(self, t: T) -> Self::Output {
        self + t
    }
}

impl Foo<MySimpleType> for MyComplexType {
    type Output = Self;
    fn foo(self, t: MySimpleType) -> Self::Output {
        let t: Self = From::from(t);
        self + t
    }
}

impl Foo<MyComplexType> for MySimpleType {
    type Output = MyComplexType;
    fn foo(self, t: MyComplexType) -> Self::Output {
        let s: MyComplexType = From::from(self);
        s + t
    }
}

これは、n引数を持つ関数が必要になるまでは最善の解決策のようです。それなら、2^n - n + 1 implステートメントを書かなければならないからです。もちろん、2つ以上のタイプを検討すると、これはさらに悪化します。

===

編集:

私のコードでは、複数のネストされた関数呼び出しがあり、単純型の関数の評価は複雑型の場合は安価で高価であるため、不要な型の昇格は避けたいと思います。@MatthieuMを使用する。の提案された解決策では、これは達成されていません。次の例を検討してください

#![feature(core_intrinsics)]

use std::ops::Add;

trait Promote<Target> {
    fn promote(self) -> Target;
}

impl<T> Promote<T> for T {
    fn promote(self) -> T {
        self
    }
}

impl Promote<u64> for u32 {
    fn promote(self) -> u64 {
        self as u64
    }
}

fn foo<Result, Left, Right>(left: Left, right: Right) -> Result
    where Left: Promote<Result>,
        Right: Promote<Result>,
        Result: Add<Output = Result>
{
    println!("============\nFoo called");
    println!("Left: {}", unsafe { std::intrinsics::type_name::<Left>() });
    println!("Right: {}",
            unsafe { std::intrinsics::type_name::<Right>() });
    println!("Result: {}",
            unsafe { std::intrinsics::type_name::<Result>() });
    left.promote() + right.promote()
}

fn bar<Result, Left, Right>(left: Left, right: Right) -> Result
    where Left: Promote<Result>,
        Right: Promote<Result>,
        Result: Add<Output = Result>
{
    left.promote() + right.promote()
}

fn baz<Result, A, B, C, D>(a: A, b: B, c: C, d: D) -> Result
    where A: Promote<Result>,
        B: Promote<Result>,
        C: Promote<Result>,
        D: Promote<Result>,
        Result: Add<Output = Result>
{
    let lhs = foo(a, b).promote();
    let rhs = bar(c, d).promote();
    lhs + rhs
}

fn main() {
    let one = baz(1u32, 1u32, 1u64, 1u32);
    println!("{}", one);
}
マシューM。

プロモーションを実装する最も簡単な方法は、Promoteトレイトを作成することだと思います。

trait Promote<Target> {
    fn promote(self) -> Target;
}

impl<T> Promote<T> for T {
    fn promote(self) -> T { self }
}

注:すべてのタイプを自分自身にプロモートできるため、包括的な実装を提供します。

単一のタイプを複数のタイプにプロモートできるため、関連するタイプを使用することはここではオプションではありません。したがって、通常の型パラメーターを使用するだけです。


これを使用した簡単な例は次のとおりです。

impl Promote<u64> for u32 {
    fn promote(self) -> u64 { self as u64 }
}

fn add<Result, Left, Right>(left: Left, right: Right) -> Result
    where
        Left: Promote<Result>,
        Right: Promote<Result>,
        Result: Add<Output = Result>
{
    left.promote() + right.promote()
}

fn main() {
    let one: u32 = add(1u32, 1u32);
    let two: u64 = add(1u32, 2u64);
    let three: u64 = add(2u64, 1u32);
    let four: u64 = add(2u64, 2u64);
    println!("{} {} {} {}", one, two, three, four);
}

唯一の問題は、2つのu32引数の場合、結果タイプを指定する必要があることです。指定しないと、コンパイラーはPromote使用可能な実装を選択できませんPromote<u32>またはPromote<u64>

これが実際の問題であるかどうかはわかりませんが、ある時点で型推論を固定するための具体的な型が必要になるためです。例えば:

fn main() {
    let v = vec![add(1u32, 1u32), add(1u32, 2u64)];
    println!("{:?}", v);
}

型ヒントなしでコンパイルできます。これadd(1u32, 2u64)は、のみであるu64ため、aVecは同種のコレクションであるadd(1u32, 1u32)ため、u64ここを返す必要があります。


ただし、経験したように、型推論で処理できる範囲を超えて結果を指示する機能が必要になる場合があります。それは大丈夫です、あなたはそれのために別の特性が必要です:

trait PromoteTarget {
    type Output;
}

impl<T> PromoteTarget for (T, T) {
    type Output = T;
}

そして、少しの実装:

impl PromoteTarget for (u32, u64) {
    type Output = u64;
}

impl PromoteTarget for (u64, u32) {
    type Output = u64;
}

これでbaz、すべての中間タイプを正しく説明するように署名を書き直すことができます。残念ながら、where句にエイリアスを導入する方法がわからないので、自分をしっかりと支えてください。

fn baz<Result, A, B, C, D>(a: A, b: B, c: C, d: D) -> Result
    where
        A: Promote<<(A, B) as PromoteTarget>::Output>,
        B: Promote<<(A, B) as PromoteTarget>::Output>,
        C: Promote<<(C, D) as PromoteTarget>::Output>,
        D: Promote<<(C, D) as PromoteTarget>::Output>,
        (A, B): PromoteTarget,
        (C, D): PromoteTarget,
        <(A, B) as PromoteTarget>::Output: Promote<Result> + Add<Output = <(A, B) as PromoteTarget>::Output>,
        <(C, D) as PromoteTarget>::Output: Promote<Result> + Add<Output = <(C, D) as PromoteTarget>::Output>,
        Result: Add<Output = Result>
{
    let lhs = foo(a, b).promote();
    let rhs = bar(c, d).promote();
    lhs + rhs
}

ここ遊び場にリンクして、結果を確認できます。

============
Foo called
Left: u32
Right: u32
Result: u32
4

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

分類Dev

関数の引数の型から定義されたマッピングに基づいて関数の戻り値の型を定義することは可能ですか?

分類Dev

引数の型に基づいて関数の戻り値の型をキャストする方法は?

分類Dev

関数のパラメーター値に基づいて、C ++で関数の戻り値の型を動的に変更する方法はありますか?

分類Dev

mypyは、現在のオブジェクトの型に基づいてメソッドの戻り値の型を選択できますか?

分類Dev

Typescript:引数の型に基づいて関数の戻り値の型を取得するにはどうすればよいですか?

分類Dev

トレイト定義で関数の戻り値の型として「impl Trait」を使用することは可能ですか?

分類Dev

トレイト定義で関数の戻り値の型として「impl Trait」を使用することは可能ですか?

分類Dev

トレイト定義で関数の戻り値の型として「impl Trait」を使用することは可能ですか?

分類Dev

トレイト定義で関数の戻り値の型として「impl Trait」を使用することは可能ですか?

分類Dev

引数の型に基づいて戻り値を取得するにはどうすればよいですか?

分類Dev

テンプレート関数が戻り値の型のみに基づいているのはなぜC ++で機能するのですか?

分類Dev

それは直接変数にメソッドの戻り値を代入することは可能ですか?

分類Dev

変数の型をその値の型に基づいて動的に変更することは可能ですか?

分類Dev

TypeScriptに、戻り値に基づいて関数の引数の型を認識させるにはどうすればよいですか?

分類Dev

Observableの戻り値に基づいてプレゼンターメソッドを単体テストするにはどうすればよいですか?

分類Dev

関数の戻り値の型を変数の型として使用することは可能ですか?

分類Dev

テンプレート引数に基づいて特定の値をメンバーコンストラクターに渡すことはできますか?

分類Dev

voidメソッドの戻り値を変数に割り当てることのポイントは何ですか?

分類Dev

eBPFまたはperfを使用して、トレースされた個々の関数に費やされた時間を計算することは可能ですか?

分類Dev

パラメータ化された型に基づいて、関数に引数と戻り値の型の制限を強制する

分類Dev

型パラメーターのtype引数に基づいてポリモーフィックな戻り値の型を持つ関数を作成するにはどうすればよいですか?

分類Dev

TypeScript:関数の引数の型に基づいて関数の戻り値の型を設定する方法

分類Dev

Javaメソッドの戻り値の型がintの場合、メソッドはバイト型の値を返すことができますか?

分類Dev

Javaメソッドの戻り値の型がintの場合、メソッドはバイト型の値を返すことができますか?

分類Dev

JavaScriptには、開始、停止、および必要な戻り値の数に基づいて数値の配列を返すメソッドがありますか?

分類Dev

Javaメソッドには、戻り値の型に基づいて異なるメモリサイズが割り当てられていますか?

分類Dev

計算されたvue変数をビューポートサイズに基づいて変更することは可能ですか?

分類Dev

C ++ 11では、戻り値の型に基づいて関数オブジェクトを受け取る関数テンプレートを特殊化するにはどうすればよいですか?

分類Dev

Scalaでメソッドの戻り値の型または変数の型を明示的に提供する利点

Related 関連記事

  1. 1

    関数の引数の型から定義されたマッピングに基づいて関数の戻り値の型を定義することは可能ですか?

  2. 2

    引数の型に基づいて関数の戻り値の型をキャストする方法は?

  3. 3

    関数のパラメーター値に基づいて、C ++で関数の戻り値の型を動的に変更する方法はありますか?

  4. 4

    mypyは、現在のオブジェクトの型に基づいてメソッドの戻り値の型を選択できますか?

  5. 5

    Typescript:引数の型に基づいて関数の戻り値の型を取得するにはどうすればよいですか?

  6. 6

    トレイト定義で関数の戻り値の型として「impl Trait」を使用することは可能ですか?

  7. 7

    トレイト定義で関数の戻り値の型として「impl Trait」を使用することは可能ですか?

  8. 8

    トレイト定義で関数の戻り値の型として「impl Trait」を使用することは可能ですか?

  9. 9

    トレイト定義で関数の戻り値の型として「impl Trait」を使用することは可能ですか?

  10. 10

    引数の型に基づいて戻り値を取得するにはどうすればよいですか?

  11. 11

    テンプレート関数が戻り値の型のみに基づいているのはなぜC ++で機能するのですか?

  12. 12

    それは直接変数にメソッドの戻り値を代入することは可能ですか?

  13. 13

    変数の型をその値の型に基づいて動的に変更することは可能ですか?

  14. 14

    TypeScriptに、戻り値に基づいて関数の引数の型を認識させるにはどうすればよいですか?

  15. 15

    Observableの戻り値に基づいてプレゼンターメソッドを単体テストするにはどうすればよいですか?

  16. 16

    関数の戻り値の型を変数の型として使用することは可能ですか?

  17. 17

    テンプレート引数に基づいて特定の値をメンバーコンストラクターに渡すことはできますか?

  18. 18

    voidメソッドの戻り値を変数に割り当てることのポイントは何ですか?

  19. 19

    eBPFまたはperfを使用して、トレースされた個々の関数に費やされた時間を計算することは可能ですか?

  20. 20

    パラメータ化された型に基づいて、関数に引数と戻り値の型の制限を強制する

  21. 21

    型パラメーターのtype引数に基づいてポリモーフィックな戻り値の型を持つ関数を作成するにはどうすればよいですか?

  22. 22

    TypeScript:関数の引数の型に基づいて関数の戻り値の型を設定する方法

  23. 23

    Javaメソッドの戻り値の型がintの場合、メソッドはバイト型の値を返すことができますか?

  24. 24

    Javaメソッドの戻り値の型がintの場合、メソッドはバイト型の値を返すことができますか?

  25. 25

    JavaScriptには、開始、停止、および必要な戻り値の数に基づいて数値の配列を返すメソッドがありますか?

  26. 26

    Javaメソッドには、戻り値の型に基づいて異なるメモリサイズが割り当てられていますか?

  27. 27

    計算されたvue変数をビューポートサイズに基づいて変更することは可能ですか?

  28. 28

    C ++ 11では、戻り値の型に基づいて関数オブジェクトを受け取る関数テンプレートを特殊化するにはどうすればよいですか?

  29. 29

    Scalaでメソッドの戻り値の型または変数の型を明示的に提供する利点

ホットタグ

アーカイブ