【问题标题】:How to match on data type in Rust?如何匹配 Rust 中的数据类型?
【发布时间】:2017-01-11 17:03:50
【问题描述】:

我正在尝试匹配结构的通用字段的数据类型并做出相应的反应。我的总体思路是这样的(代码无法编译):

struct Foo<T> {
    bar: T,
}

fn main() {
    let x = Foo::<String> {
        bar: "world".to_string(),
    };

    match x.bar {
        String => println!("It's a string!"),
        u32 => println!("It's a u32!"),
        _ => println!("Something else"),
    };

    println!("end of program!");
}

来自编译器的错误信息:

warning: unreachable pattern
  --> src/main.rs:12:9
   |
11 |         String => println!("It's a string!"),
   |         ------ matches any value
12 |         u32 => println!("It's a u32!"),
   |         ^^^ unreachable pattern
   |
   = note: `#[warn(unreachable_patterns)]` on by default

warning: unreachable pattern
  --> src/main.rs:13:9
   |
11 |         String => println!("It's a string!"),
   |         ------ matches any value
12 |         u32 => println!("It's a u32!"),
13 |         _ => println!("Something else"),
   |         ^ unreachable pattern

warning: unused variable: `String`
  --> src/main.rs:11:9
   |
11 |         String => println!("It's a string!"),
   |         ^^^^^^ help: consider prefixing with an underscore: `_String`
   |
   = note: `#[warn(unused_variables)]` on by default

warning: unused variable: `u32`
  --> src/main.rs:12:9
   |
12 |         u32 => println!("It's a u32!"),
   |         ^^^ help: consider prefixing with an underscore: `_u32`

warning: variable `String` should have a snake case name
  --> src/main.rs:11:9
   |
11 |         String => println!("It's a string!"),
   |         ^^^^^^ help: convert the identifier to snake case: `string`
   |
   = note: `#[warn(non_snake_case)]` on by default

我想要x 匹配第一个。我其实并不确定自己想做的事情能不能做到,但是怎样才能达到预期的效果呢?

【问题讨论】:

  • 简短的回答是“不是马上”。您可能会发现这个问题很有用:How does Rust implement reflection?
  • Rust 将CamelCase 用于结构、枚举和特征。你应该使用Foo

标签: rust


【解决方案1】:

惯用解决方案

创建一个在Foo 中约束参数T 的特征,实现任何特定行为作为该特征的关联函数。

例子:

trait PrintMe {
    fn print_me(&self);
}

impl PrintMe for String {
    fn print_me(&self) { println!("I am a string"); }
}

struct Foo<T: PrintMe> {
    bar: T
}

fn main() {
    // ...
    x.bar.print_me();
}

这是有原则的泛型编程,您可以准确地声明可能的泛型参数的行为差异,这样就不会感到意外。

另见:


精确解

Rust 确实可以查询类型:每种类型都有一个唯一的 TypeId 关联,您可以在 TypeId 上匹配一系列 if 检查。很笨重。

fn print_me<T>(x: &Foo<T>) {
    if TypeId::of::<T>() == TypeId::of::<String>() {
        println!("I am a string");
    } else // ...
}

但是请...不要那样做:)

【讨论】:

  • 谢谢你,马修。这样就搞清楚了。我认为我需要总体上改变我的设计,我一直在布置我的代码“C 风格”。谢谢!
  • +1 表示“不要那样做”,我只想指出 Rust enum,这是一个更好的(更短,更清晰,惯用和编译时检查)编写所有这些TypeId::of 检查的方式。
  • @trentcl:这取决于。 enum 用于封闭多态,而trait 用于开放多态......如果这有意义的话。
  • @MatthieuM。这说得通。不过,您已经提到了特征,所以我想我会指出其他解决方案
【解决方案2】:

更全面地了解类型匹配

基本上,您几乎无法匹配类型,而是必须使用 rust 的 traits

这里是对 Rust 特征的有用介绍:
https://doc.rust-lang.org/rust-by-example/trait.html

我将简单地解释如何匹配类型,所以如果你不知道如何使用特征也没关系。

以下是根据类型更普遍地改变行为的方法:

  1. 根据类型定义一个将做某事的特征。

  2. 添加一个函数来接收你需要做的事情。 它将采用类型变化为self 的变量。不要在该函数中添加正文,只需在定义后面加上分号;

    trait DoSomething {
        fn someFunction(&self);
    }
    
  3. 为您想要匹配的所有类型实现特征:Stringu32。 这是您可以为每种类型编写不同行为的部分。

    impl DoSomething for String {
        fn someFunction(&self) {
            println!("It is a string!")
        }
    }
    impl DoSomething for u32 {
        fn someFunction(&self) {
            println!("It is a u32!")
        }
    }
    
  4. 不是在一个元素上匹配,而是调用它的特征中的方法。

    struct Foo<T> {
        bar: T,
    }
    
    fn main() {
        let x = Foo::<String> {
            bar: "world".to_string(),
        };
    
        // the call of the method someFunction will differentiate the type of x.bar
        x.bar.someFunction();
    }
    

TLDR:在 Rust 中,为了匹配类型,我们创建一个 trait,为每种类型实现一个函数并在要匹配的元素上调用它。

【讨论】:

  • 用反引号将其包围以将其标记为代码。内联代码的单反引号,代码块的三个反引号。
猜你喜欢
  • 1970-01-01
  • 2016-03-16
  • 2020-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多