Rust in Everything
#Rust

结构体中的元组结构体(tuple struct)

在Rust中,struct StructName(DemoName); 这种用法定义的事一个元组结构体(tuple struct), 它是一种特殊的结构体, 它的字段没有名称,只有索引。

它并不会直接继承DemoName中所有pub内容,而是将DemoName作为一个单独的字段封装在结构体中。

为什么要这样做呢?这样做的好处是可以在不改变DemoName的情况下,为其添加额外的功能。

元组结构体的定义和使用

struct DemoName {
	pub index: i32,
	pub value: String,
}

struct StructName(DemoName);

在上面的例子中,StructName是一个元组结构体,它包含一个DemoName类型的字段。 这个字段可以通过索引0来访问。

fn main() {
	let demo = DemoName {
		index: 1,
		value: "Hello".to_string(),
	};
	let struct_name = StructName(demo);
	
	// 访问内部的`DemoName`结构体
	println!("Index: {}", struct_name.0.index);
	println!("Value: {}", struct_name.0.value);
}

在这个例子中,我们创建了一个DemoName实例, 并将其作为一个字段封装在StructName结构体中。 要访问DemoName中的字段,我们使用struct_name.0来访问。

自动解引用和Deref

通过实现Deref trait,可以使元组结构体在使用时表现的像它所包含的值一样,从而简化代码。

use std::ops::Deref;

struct DemoName {
	pub index: i32,
	pub value: String,
}

struct StructName(DemoName);

impl Deref for StructName {
	type Target = DemoName;

	fn deref(&self) -> &Self::Target {
		&self.0
	}
}

fn main() {
	let demo = DemoName {
		index: 1,
		value: "Hello".to_string(),
	};
	let struct_name = StructName(demo);
	
	// 访问内部的`DemoName`结构体
	println!("Index: {}", struct_name.index);
	println!("Value: {}", struct_name.value);
}

在这个例子中,通过实现Deref trait,我们可以直接访问StructName中的DemoName字段, 而不需要显式地使用索引0。

结构体的创建和使用方式

普通结构体

普通结构体的字段有名字,可以通过名字来访问。

struct DemoName {
	pub index: i32,
	pub value: String,
}

fn main() {
	let demo = DemoName {
		index: 1,
		value: "Hello".to_string(),
	};
	
	// 访问结构体的字段
	println!("Index: {}", demo.index);
	println!("Value: {}", demo.value);
}

元素结构体

元组结构体的字段没有名字,只有索引,可以通过索引来访问。

struct DemoName (i32, String);

fn main() {
	let demo = DemoName(1, "Hello".to_string());
	
	// 访问元组结构体的字段
	println!("Index: {}", demo.0);
	println!("Value: {}", demo.1);
}

单元结构体

单元结构体没有字段,通常用于标记或实现某些trait。

struct UnitStruct;

fn main() {
	let unit = UnitStruct;
}

实际使用案例: JWT签名和验证

use crate::{AppError, User};
use jwt_simple::prelude::*;
use std::ops::Deref;

const JWT_DURATION: u64 = 60 * 60 * 24 * 7;
const JWT_ISS: &str = "chat_server";
const JWT_AUD: &str = "chat_web";

pub struct EncodingKey(Ed25519KeyPair);
pub struct DecodingKey(Ed25519PublicKey);

impl EncodingKey {
  pub fn load(pem: &str) -> Result<Self, AppError> {
    Ok(Self(Ed25519KeyPair::from_pem(pem)?))
  }

  pub fn sign(&self, user: impl Into<User>) -> Result<String, AppError> {
    let claims = Claims::with_custom_claims(
      user.into(), Duration::from_secs(JWT_DURATION));
    let claims = claims.with_issuer(JWT_ISS).with_audience(JWT_AUD);
    Ok(self.0.sign(claims)?)
  }
}

impl DecodingKey {
  pub fn load(pem: &str) -> Result<Self, AppError> {
    Ok(Self(Ed25519PublicKey::from_pem(pem)?))
  }

  pub fn verify(&self, token: &str) -> Result<User, AppError> {
    let mut options = VerificationOptions::default();
    options.allowed_issuers = Some(HashSet::from_strings(&[JWT_ISS]));
    options.allowed_audiences = Some(HashSet::from_strings(&[JWT_AUD]));
    let claims = self.0.verify_token::<User>(token, Some(options))?;
    Ok(claims.custom)
  }
}

impl Deref for EncodingKey {
  type Target = Ed25519KeyPair;

  fn deref(&self) -> &Self::Target {
    &self.0
  }
}

impl Deref for DecodingKey {
  type Target = Ed25519PublicKey;

  fn deref(&self) -> &Self::Target {
    &self.0
  }
}

#[cfg(test)]
mod tests {
  use anyhow::Result;
  use super::*;
  use crate::User;

  #[tokio::test]
  async fn test_jwt() -> Result<()> {
    let encoding_pem = include_str!("../../fixtures/encoding.pem");
    let decoding_pem = include_str!("../../fixtures/decoding.pem");
    let ek = EncodingKey::load(encoding_pem)?;
    let dk = DecodingKey::load(decoding_pem)?;

    let user = User::new(1, "Alice", "alice@example.com");

    let token = ek.sign(user.clone())?;
    let user2 = dk.verify(&token)?;
    assert_eq!(user, user2);
    Ok(())
  }
}

在这个例子中,我们定义了两个结构体EncodingKey和DecodingKey, 它们分别用于签名和验证JWT令牌。 这里使用EncodingKey封装了Ed25519KeyPair,使其方便加载密钥和签名。 这里使用DecodingKey封装了Ed25519PublicKey,使其方便加载公钥和验证。

通过实现Deref trait,我们可以直接访问Ed25519KeyPair和Ed25519PublicKey中的方法, 而不需要显式地使用self.0来访问。

总结

本文介绍了元组结构体(tuple struct)的定义和使用,以及如何通过实现Deref trait来简化代码。 元组结构体是一种特殊的结构体,它的字段没有名称,只有索引。 通过实现Deref trait,可以使元组结构体在使用时表现的像它所包含的值一样,从而简化代码。

评论区

登录后即可发表评论

加载评论中…

💬 关联讨论

发起讨论
加载中...