Rust 枚举和匹配:它如何区分两个不同的枚举,它们采用两个不同的结构但内容相同

Rust enum and match : How does it distinguishes two different enums that takes in two different struct but same contents

我试图理解以下枚举 from this repo

#[repr(C)]
#[derive(BorshSerialize, BorshDeserialize, Debug, Clone)]
pub struct InitEscrowArgs {
  pub data: EscrowReceive,
}

#[repr(C)]
#[derive(BorshSerialize, BorshDeserialize, Debug, Clone)]
pub struct ExchangeArgs {
  pub data: EscrowReceive,
}

#[derive(BorshSerialize, BorshDeserialize, Clone)]
pub enum EscrowInstruction {
  InitEscrow(InitEscrowArgs),

  Exchange(ExchangeArgs),

  CancelEscrow(),
}

它在这场比赛中使用了它 from this repo

  pub fn process(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
  ) -> ProgramResult {
    let instruction = EscrowInstruction::try_from_slice(instruction_data)?;

    match instruction {
      EscrowInstruction::InitEscrow(args) => {
        msg!("Instruction: Init Escrow");
        Self::process_init_escrow(program_id, accounts, args.data.amount)
      }
      EscrowInstruction::Exchange(args) => {
        msg!("Instruction: Exchange Escrow");
        Self::process_exchange(program_id, accounts, args.data.amount)
      }
      EscrowInstruction::CancelEscrow() => {
        msg!("Instruction: Cancel Escrow");
        Self::process_cancel(program_id, accounts)
      }
    }
  }

我知道这个 try_from_slice 方法获取某种字节数组并将其反序列化。

我不明白它如何确定要使用的枚举值。

枚举有 3 个选择,InitEscrow / Exchange / CancelEscrow,但是什么决定了匹配要知道它应该是哪一个 select?

在我看来,InitEscrowArgs 和 ExchangeArgs 都采用相同的结构。两者都包含 EscrowReceive 数据类型的数据。

方法 try_from_sliceBorshDeserialize 特征的一部分,它是在相关枚举上派生的。因此,枚举变体之间的选择是由反序列化器的实现做出的。

为了了解实际情况,我构建了最简单的示例:

use borsh::BorshDeserialize;

#[derive(BorshDeserialize)]
enum Enum {
    Variant1(u8),
    Variant2,
}

通过使用cargo expand和一些手动清理,我们可以得到以下等效代码:

impl borsh::de::BorshDeserialize for Enum {
    fn deserialize(buf: &mut &[u8]) -> Result<Self, std::io::Error> {
        let variant_idx: u8 = borsh::BorshDeserialize::deserialize(buf)?;
        let return_value = match variant_idx {
            0u8 => Enum::Variant1(borsh::BorshDeserialize::deserialize(buf)?),
            1u8 => Enum::Variant2,
            _ => {
                let msg = format!("Unexpected variant index: {}", variant_idx);
                return Err(std::io::Error::new(
                    std::io::ErrorKind::InvalidInput,
                    msg,
                ));
            }
        };
        Ok(return_value)
    }
}

其中内部 deserialize 调用指的是 impl BorshDeserialize for u8:

fn deserialize(buf: &mut &[u8]) -> Result<Self> {
    if buf.is_empty() {
        return Err(Error::new(
            ErrorKind::InvalidInput,
            ERROR_UNEXPECTED_LENGTH_OF_INPUT,
        ));
    }
    let res = buf[0];
    *buf = &buf[1..];
    Ok(res)
}

因此,它的工作方式如下:

  • 解串器尝试从输入中提取一个字节;如果有 none - 这是一个错误。
  • 这个字节被解释为枚举变体的索引;如果它与其中一个变体不匹配 - 这是一个错误。
  • 如果变体包含任何数据,反序列化器会尝试从输入中提取该数据;如果失败(根据内部类型的实现)——这是一个错误。