< 返回版块

cybertheye 发表于 2024-03-05 17:30

Tags:enum

在学 https://practice.course.rs/compound-types/enum.html 第1和第2

第1,我觉得就是枚举变体的序列号可以自定义的意思是吧,默认从0开始

看了第2题后,我想能不能放一块

  enum PokerCard {
      Clubs(u8) = 4,
      Spades(u8) = 3,
      Diamonds(char) = 2,
      Hearts(char) = 1,
  }
  fn main(){
      let c1 = PokerCard::Spades(5);
      let c2 = PokerCard::Diamonds('A');
      println!("{}",PokerCard::Spades as u8);
      //println!("{:?}",c1);
  }

就类似这样 但编译报错

error[E0732]: `#[repr(inttype)]` must be specified
 --> src/main.rs:3:1
  |
3 | enum PokerCard {
  | ^^^^^^^^^^^^^^

For more information about this error, try `rustc --explain E0732`.
error: could not compile `cargoQ1Oc0V` (bin "cargoQ1Oc0V") due to 1 previous error

这是不允许的是吗?

然后查看了 E0732

说是要加上 `#[repr(inttype)]

  #[repr(u8)]
  enum PokerCard {
      Clubs(u8) = 4,
      Spades(u8) = 3,
      Diamonds(char) = 2,
      Hearts(char) = 1,
  }
  fn main(){
      let c1 = PokerCard::Spades(5);
      let c2 = PokerCard::Diamonds('A');
      println!("{}",PokerCard::Spades as u8);
      //println!("{:?}",c1);
  }

这样好像可以通过编译了 但问题

  1. 为啥这里要加个这个,
  enum Number {
      Zero,
      One,
      Two,
  }

  enum Number1 {
      Zero = 0,
      One,
      Two = 222,
  }

这样就没加也可以

  1. 运行结果怎么是 96?? 期待是3 playground

评论区

写评论
苦瓜小仔 2024-03-06 11:58

那为啥上面这段代码可以输出?

元组型 variant,你注意到我说的定语“元组”了吗?

When referred to, a function item, or the constructor of a tuple-like struct or enum variant, yields a zero-sized value of its function item type.

src: https://doc.rust-lang.org/reference/types/function-item.html

不同种类的 enum,对应的 discriminant 规则是不一样的(无数据的 enum 的确可以 as integer 来充当判别式,但有数据的 enum 会不一样):

如果想比较同类 varaint,discriminant 或许是你想要的。

use std::mem::discriminant;
#[repr(u8)]
enum PokerCard {
    Clubs(u8) = 4,
    Spades(u8) = 3,
    Diamonds(char) = 2,
    Hearts(char) = 1,
}
fn main() {
    let c1 = PokerCard::Spades(5);
    let c2 = PokerCard::Diamonds('A');
    dbg!(
        discriminant(&c1) != discriminant(&c2),
        discriminant(&c1) == discriminant(&PokerCard::Spades(3))
    );
}
asuper 2024-03-06 09:56

应该是只有简单的enum才可以表达为整数,就是不含子元素的。不过即使是这种含子元素的,enum内部实现是有一个整数类型tag字段的,调试模式的时候可以看到,具体怎么用我就不清楚了。

这里评论区的讨论我绝对很有用

关于枚举和整数的转换,可以参考Rust圣经

作者 cybertheye 2024-03-06 08:52
enum Number {
    Zero,
    One,
    Two,
}

enum Number1 {
    Zero = 0,
    One,
    Two,
}

// C-like enum
enum Number2 {
    Zero = 0,
    One = 1,
    Two = 2,
}


fn main() {
    // a enum variant can be converted to a integer by `as`
    assert_eq!(Number::One as u8, Number1::One as u8);
    assert_eq!(Number1::One as u8, Number2::One as u8);
} 

那为啥上面这段代码可以输出?

--
👇
苦瓜小仔: 那只是一个函数指针的地址而已,只不过被溢出了。结果不同取决于整数溢出的配置方式。

元组结构体和元组型 variant,都可以作为构造函数,而函数与函数指针隐式强制转换,所以你的代码变成了

println!("{}", PokerCard::Spades as (fn(_) -> _) as u8);

苦瓜小仔 2024-03-06 01:07

那只是一个函数指针的地址而已,只不过被溢出了。结果不同取决于整数溢出的配置方式。

元组结构体和元组型 variant,都可以作为构造函数,而函数与函数指针隐式强制转换,所以你的代码变成了

println!("{}", PokerCard::Spades as (fn(_) -> _) as u8);

Bai-Jinlin 2024-03-05 18:33

PokerCard::Spades其实是一个函数,你as出来的其实是个函数地址。 要想得到判别式可以自己按照对应类型的布局的实现自己进行指针操作。

#![feature(unboxed_closures)]
#[derive(Debug,PartialEq)]
#[repr(u8)]
enum PokerCard {
    Clubs(u8) =3,
    Spades(u8) =7,
    Diamonds(char) =5,
    Hearts(char) =6,
}
impl PokerCard{
    fn discriminant(&self) -> u8 {
        unsafe { *(self as *const Self as *const u8) }
    }
}
fn main(){
    unsafe{
        let f:extern "rust-call" fn((u8,)) -> PokerCard=core::mem::transmute(PokerCard::Spades as usize);
        let r = f((77u8,));
        assert_eq!(PokerCard::Spades(77),r);
        assert_eq!(7,r.discriminant());
    }
}
songzhi 2024-03-05 17:47

stable 和 nightly 的输出结果还不一样,我本地 nightly 的结果也不一样。所以这种写法似乎是 undefined behavior。

1 共 6 条评论, 1 页