在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(())
}
}
在这个例子中,我们定义了两个结构体EncodingKey
和DecodingKey
,
它们分别用于签名和验证JWT令牌。
这里使用EncodingKey
封装了Ed25519KeyPair
,使其方便加载密钥和签名。
这里使用DecodingKey
封装了Ed25519PublicKey
,使其方便加载公钥和验证。
通过实现Deref
trait,我们可以直接访问Ed25519KeyPair
和Ed25519PublicKey
中的方法,
而不需要显式地使用self.0
来访问。
总结
本文介绍了元组结构体(tuple struct
)的定义和使用,以及如何通过实现Deref
trait来简化代码。
元组结构体是一种特殊的结构体,它的字段没有名称,只有索引。
通过实现Deref
trait,可以使元组结构体在使用时表现的像它所包含的值一样,从而简化代码。