Rust Enum

Table of Contents

1. Rust Enum

1.1. union

1.1.1. untagged union

union 分为两种: untagged union 及 tagged union.

c 语言的 union 是 untagged union:

union Data {
    int i;
    float f;
    char c[4];
};

int main(int argc, char *argv[]) {
    union Data x;
    x.i = 0x01020304;

    int i;
    for (i = 0; i < 4; i++) {
        printf("%x ", x.c[i]);
    }
}

untagged, 是指 Data 本身没有保存数据的具体类型 (tag), 所以无法区分数据的类型.

1.1.2. tagged union

rust 的 enum 实际是 tagged union

#[derive(Debug)]
enum Data {
    i(i32),
    f(i32),
}
fn main() {
    let x = Data::i(0x1234);
    println!("size: {:?}", std::mem::size_of::<Data>());

    let pp = &x as *const _ as *const [i32; 2];
    unsafe {
        println!("Data:i(0x1234): {:?}", (*pp));
    }

    let x = Data::f(0x1234);
    let pp = &x as *const _ as *const [i32; 2];
    unsafe {
        println!("Data:f(0x1234): {:?}", (*pp));
    }
}
size: 8
Data:i(0x1234): [0, 4660]
Data:f(0x1234): [1, 4660]

可见 Data 的内存布局为 [tag, data], Data 的具体类型是做为 tag 保存在 Data 中

1.2. enum overview

enum Data {
    A,
    B(i32),
    C(i32, i32),
    D(Vec<i32>),
}

enum Data2<T> {
    A,
    B(T),
}

fn main() {
    let x = Data::D(vec![1, 2, 3]);
    let y = Data2::B(10);
}

1.3. pattern matching

  1. pattern matching 主要用来 match enum, 但可以用来 match 普通变量

    fn test(x: i32) -> () {
        match x {
            1 => println!("{:?}", 1),
            2 | 3 | 4 | 5 => println!("{:?}", "2|3|4|5"),
            6...7 => println!("{:?}", "[6..7]"),
            x if x >= 8 && x <= 9 => println!("{:?}", "x>8"),
            y => println!("other:{:?}", y),
        }
    }
    
    fn main() {
        for i in 1..=10 {
            test(i);
        }
    }
    
  2. pattern matching 还可以用来 destructure struct 和 enum:

    enum Data {
        A(i32, i32),
        B(String),
    }
    
    struct Test {
        x: i32,
        y: i32,
    }
    fn main() {
        match (Test { x: 1, y: 1 }) {
            Test { x, y: 1 } => {
                println!("Test with y=1");
            }
            Test { x, y } => {
                println!("Test with {}:{}", x, y);
            }
        }
    
        match (Data::A(1, 1)) {
            Data::A(x, 1) => {
                println!("Data::A {}", x);
            }
            _ => (),
        }
    }
    
  3. 使用 ref 来 destructure struct

    #[derive(Debug)]
    struct Test {
        s: String,
    }
    
    fn main() {
        let t = Test {
            s: "hello".to_owned(),
        };
        match &t {
            &Test {ref s } => {
                println!("{:?}", s);
            }
            _ => (),
        }
    }
    
  4. 使用 `&` 来 destructure struct

    struct Test(&'static i32);
    fn foo(x: i32) -> () {
        println!("{:?}", x);
    }
    
    fn main() {
        match Test(&10) {
            Test(&x) => {
                foo(x);
            }
        }
    }
    

    `&x` 的实际上是把 `&10` 作 dereference 的结果赋值给 x, 因为 `&x match &10`

    #[derive(Debug)]
    struct Inner(i32);
    struct Outer<'a>(&'a Inner);
    
    fn main() {
        let inner = Inner(10);
        match Outer(&inner) {
            Outer(&i) => {
                println!("{:?}", i);
            }
        }
    }
    

    `&i` 会导致 `i=*(Outer.0)`, 因此编译报错, 因为 Outer.0 作为 borrowed content 无法被 `move out of Outer`

Author: [email protected]
Date: 2019-02-26 Tue 00:00
Last updated: 2024-08-05 Mon 17:58

知识共享许可协议