跳到主要内容

结构体中的元组结构体(`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, "Hal", "halzzz@gmail.com");

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

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

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

总结

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