2 个版本
0.0.1 | 2020 年 5 月 25 日 |
---|---|
0.0.0 | 2020 年 5 月 22 日 |
#34 在 #重用 中
28KB
203 行
重用(结构体,枚举)
它提供了函数式宏,可以从 结构体 和 枚举 定义中重用字段。
[dependencies]
born = "0.0.1"
为什么选择这个库?
您可以在 Rust 结构体和枚举中一次性定义常用字段,并通过重用它们来减少代码重复。当您想重用相同字段的多个结构体时,可以使用此功能,如下例所示。
use born::{
nested_macro,
public_struct,
};
public_struct!(
pub struct UserBase {
username: String,
email: String,
full_name: Option<String>,
}
);
UserBase!(
pub struct UserIn {
pub password: String,
}
);
// Reuse with the same fields.
UserBase!(
pub struct UserOut
);
UserBase!(
pub struct UserInDB {
pub hashed_password: String,
}
);
与以下来自 FAST API 的 Python 代码进行比较,该代码启发了这个库。
from pydantic import BaseModel, EmailStr
class UserBase(BaseModel):
username: str
email: EmailStr
full_name: str = None
class UserIn(UserBase):
password: str
# Reuse with the same fields.
class UserOut(UserBase):
pass
class UserInDB(UserBase):
hashed_password: str
您可以看到,在这两部分中,都执行了几乎相同的操作来减少代码重复。
但与 Python 不同,宏不会从 born 继承字段。它通过您的第一个结构体或枚举定义懒加载构建。
所有由此生成的结构体和枚举之间完全无关,除了它们都基于相同的定义之外。它们之间没有内存共享或类似的东西。
此库中的宏是 懒加载结构体和枚举构建器,用于消除代码重复。这是通过 Rust 宏的力量实现的。
示例
在此,使用了用于构建公共结构体和枚举的宏。
如果您想构建私有结构体和枚举,只需使用以 private 开头的宏即可,并且不应在内部使用 pub
。
结构体
例如,您构建一个简单的用于发送私密消息的演示 Web 服务器。
use born::{
nested_macro,
public_struct,
};
public_struct!(
// pub is required here before struct
pub struct MessageBase {
pub text: String
// pub text: String // , is not required for the struct definition.
}
);
MessageBase!(
#[derive(Debug, Clone, PartialEq)]
pub struct Message {
pub read: bool,
// read: bool, // pub is optional.
}
);
impl Message {
fn update_text(&mut self, new_message: String) {
self.text = new_message
}
fn read(&mut self) {
if self.read == false {
self.read = true;
}
}
}
MessageBase!(
#[derive(Debug, Clone, PartialEq)]
pub struct MessageCreateRequest
);
MessageBase!(
// #[derive(Debug, Clone, PartialEq)]
pub struct MessageUpdateRequest
);
fn main() {
let message_create_request = MessageCreateRequest {
text: "I am Steadylearner and 'born' is the crate name.".into(),
};
let mut message = Message {
text: message_create_request.text,
read: false,
};
println!("{:#?}", &message);
assert_eq!(message, message.clone());
let message_update_request = MessageUpdateRequest {
text: "Reuse fields with macros from 'born'.".into(),
};
message.update_text(message_update_request.text);
println!("{:#?}", &message);
message.read();
println!("{:#?}", &message);
}
枚举
use born::{
nested_macro,
private_enum,
};
private_enum!(
enum WebEventBase {
PageLoad,
PageUnload, // , here is required if you want to extend it.
}
);
WebEventBase!(
// #[derive(Debug, Clone, PartialEq)]
enum WebEvent {
KeyPress(char),
Click { x: i64, y: i64 },
Paste(String),
}
);
fn inspect(event: WebEvent) {
match event {
WebEvent::PageLoad => println!("page loaded"),
WebEvent::PageUnload => println!("page unloaded"),
WebEvent::KeyPress(c) => println!("pressed '{}'.", c),
WebEvent::Paste(s) => println!("pasted \"{}\".", s),
WebEvent::Click { x, y } => {
println!("clicked at x={}, y={}.", x, y);
},
}
}
fn main() {
let pressed = WebEvent::KeyPress('x');
let pasted = WebEvent::Paste("my text".to_owned());
let click = WebEvent::Click { x: 20, y: 80 };
let load = WebEvent::PageLoad;
let unload = WebEvent::PageUnload;
inspect(pressed);
inspect(pasted);
inspect(click);
inspect(load);
inspect(unload);
}
详情
-
从这些宏创建的每个结构体和枚举之间完全无关,除非它们都基于相同的定义。
-
当您使用
private_struct!
和private_enum!
时,您不能在内部使用 pub 关键字,并且其他人可以使用它们。 如果私有结构体或枚举可以具有公共字段,则这就不合逻辑。 -
nested_macro!
是必需的,用于使用此 crate 中的其他宏。它用于创建创建其他宏的宏。
macro_rules! nested_macro {
($($body:tt)*) => {
macro_rules! __nested_macro { $($body)+ }
__nested_macro!($);
}
}
为什么不使用属性宏?
-
您也可以重用具有属性宏的字段。但是,您需要一些依赖项。
如何测试
$git clone [email protected]:steadylearner/born.git && cargo test pass
$cargo test pass
运行通过测试。$cargo test fail
运行失败测试。您需要先安装 trybuild。
如果您想查看此包中宏的展开方式,请使用 $cargo test macros
。在使用它之前,您需要安装 rustfmt 和 cargo-expand。
$rustup component add rustfmt && cargo install cargo-expand
macrotest 基于 trybuild。它们在单个命令中测试不兼容,并且需要很长时间。
它们使 cargo 每次都重新下载依赖项并重新编译。因此,存在单独测试它们的命令。
许可证
根据您的选择,受 Apache License,版本 2.0 或 MIT 许可证 许可。除非您明确表示,否则根据 Apache-2.0 许可证定义,您有意提交以包含在此软件包中的任何贡献,应如上所述双重许可,无需任何附加条款或条件。