Why does pattern matching on a union have an unreachable pattern warning?

时间:2018-07-20 16:03:41

标签: rust pattern-matching

Given the documentation,我不明白为何联合上的模式匹配无法正常工作:

union A {
    a1: i32,
    a2: f32,
}

struct B(A);
let b = B(A { a2: 1.0 });
unsafe {
    match b.0 {
        A { a1 } => println!("int"),
        A { a2 } => println!("float"),
    }
}

Outputs "int" with an unreachable warning

warning: unreachable pattern
  --> src/main.rs:12:13
   |
12 |             A { a2 } => println!("float"),
   |             ^^^^^^^^
   |
   = note: #[warn(unreachable_patterns)] on by default

1 个答案:

答案 0 :(得分:5)

union entire 点是,编译器没有在union中存储有关其类型的任何信息。 完全取决于程序员。因此,match没有信息可用于确定该值是什么类型。

因此,您的代码在概念上等同于

struct A {
    a1: i32,
}

let b = A { a1: 42 };

match b {
    A { a1 } => println!("int {}", a1),
    A { a1 } => println!("float {}", a1),
}

在任何情况下都不会执行第二个比赛臂。

实际上,在字段之间来回切换是union的主要用法:

union A {
    i: i32,
    f: f32,
}

let a = A { i: 42 };
let b = unsafe { a.f };

println!("{}", b);

如果希望编译器跟踪所拥有的变体,则可能希望使用enum。在某些情况下,枚举被称为 tagged联合,因为这正是它们的本质:一个带有标签的联合,以标识该联合包含的内容。

否则,您需要以其他方式跟踪联合中实际上是什么类型。一种方法是实现自己的标签:

union A {
    a1: i32,
    a2: f32,
}

struct B {
    is_int: bool,
    data: A,
}

let b = B {
    is_int: false,
    data: A { a2: 1.0 },
};

unsafe {
    match b {
        B {
            is_int: true,
            data: A { a1 },
        } => println!("int {}", a1),
        B {
            is_int: false,
            data: A { a2 },
        } => println!("float {}", a2),
    }
}

标签可以是您可以匹配的任何内容:

union A {
    a1: i32,
    a2: f32,
}

struct B {
    kind: Kind,
    data: A,
}

enum Kind {
    Int,
    Float,
}

let b = B {
    kind: Kind::Float,
    data: A { a2: 1.0 },
};

unsafe {
    match b {
        B {
            kind: Kind::Int,
            data: A { a1 },
        } => println!("int {}", a1),
        B {
            kind: Kind::Float,
            data: A { a2 },
        } => println!("float {}", a2),
    }
}

我想你甚至可以在联合中使用 枚举...

union A {
    a1: i32,
    a2: f32,
}

enum B {
    Int(A),
    Float(A),
}

let b = B::Float(A { a2: 1.0 });

unsafe {
    match b {
        B::Int(A { a1 }) => println!("int {}", a1),
        B::Float(A { a2 }) => println!("float {}", a2),
    }
}