2个版本
0.1.1 | 2022年4月7日 |
---|---|
0.1.0 | 2021年12月30日 |
#158 in #merge
每月 28 次下载
用于 zero4rs
15KB
104 行
Struct-Merge
!!! 已废弃 !!!
此项目已被 inter-struct 取代。
请考虑切换到该库,因为这个库不会得到任何更新。
!!! 已废弃 !!!
生成合并各种结构的代码。
此crate提供两个proc-macros来生成两个非常相似的特质。
/// Merge another struct into Self whilst consuming it.
///
/// The other trait is named `StructMergeRef` and merges other structs by reference.
pub trait StructMerge<Src> {
/// Merge the given struct into self.
fn merge(&mut self, src: Src);
/// Nearly the same as `merge`, but any `Self::Option<T>` fields will only get merged, if the
/// value of the field is `None`.
fn merge_soft(&mut self, src: Src);
}
在使用此crate之前,请务必阅读 已知注意事项 部分!
示例
此示例展示了 struct_merge
宏的简单用法。
structs.rs
:此示例中使用的结构体。
use struct_merge::struct_merge;
/// The target struct we'll merge into.
pub struct Target {
pub normal: String,
pub optional: Option<String>,
/// This field won't be touched as the macro cannot find a
/// respective `ignored` field in the `Mixed` struct.
pub ignored: Option<String>,
}
/// A struct with both an identical and an optional field type.
/// Note that the path to `Target` must always a fully qualifying path.
#[struct_merge(crate::structs::Target)]
pub struct Mixed {
pub normal: String,
pub optional: Option<Option<String>>,
}
lib.rs
use struct_merge::prelude::*;
mod structs;
use structs::{Target, Mixed};
fn main() {
let mut target = Target {
normal: "target".to_string(),
optional: Some("target".to_string()),
ignored: "target".to_string(),
};
let mixed = Mixed {
/// Has the same type as Target::normal
normal: "mixed".to_string(),
/// Wraps Target::optional in an Option
optional: Some(Some("mixed".to_string())),
};
// Merge the `Mixed` struct into target.
target.merge(mixed);
// You can also call this:
// mixed.merge_into(target);
assert_eq!(target.normal, "mixed".to_string());
assert_eq!(target.optional, Some("mixed".to_string()));
assert_eq!(target.ignored, "target".to_string());
}
合并行为
以下将解释目标结构体单个字段的合并行为。目标字段的名称为 test
。
merge
和 merge_ref
相同类型
struct Src {
test: T
}
struct Target {
test: T
}
这会将 src.test
合并到 target.test
目标.test=src.test
目标为可选
struct Src {
test: T
}
struct Target {
test: Option<T>
}
这会将 src.test
包装成 Option
并合并到 target.test
目标.test= Some(src.test);
源为可选
struct Src {
test: Option<T>
}
struct Target {
test: T
}
如果 src.test
是 Some
,则只将 src.test
合并到 target.test
:
if let Some(value) = src.test {
target.test = value;
}
merge_soft
和 merge_ref_soft
如果 target.test
不是可选的
只要目标字段不是可选的,就不会被修改!
目标为可选
struct Src {
test: T
}
struct Target {
test: Option<T>
}
这会将 src.test
包装成 Option
并合并到 target.test
,但只有当 target.test
是 None
if target.test.is_none() {
target.test = Some(src.test);
}
两者都是可选的
struct Src {
test: Option<T>
}
struct Target {
test: Option<T>
}
此功能只会将 src.test
合并到 target.test
,前提是 target.test
是 None
: \
if target.test.is_none() {
target.test = src.test;
}
已知问题
模块/类型解析
在过程宏阶段,没有简单的方法来进行路径或类型解析。
因此,这个软件包可能无法与您项目的结构兼容。
然而,由于我们创建的是安全和有效的Rust代码,编译器将在出现任何问题时抛出错误。
在使用正常的 merge
函数时,最坏的情况是这个软件包无法编译。
然而,当使用类型别名等隐藏类型时,merge_soft
函数无法正确检测 Option
,即使值已经是 Some
也可能会合并值!
尚未解决的问题
这些问题可能可以解决,但它们不是显而易见的。
- 位于软件包根目录的Struct。例如
lib.rs
。 - 位于集成测试中的Struct。
- 位于文件中(可能嵌套或交替的)
mod {}
块中的Struct。 - 源根目录不是
src
。我们可能需要检查环境并可能解析Cargo.toml
。 - 使用不同标记但具有相同类型的不同泛型别名。例如,
Box<dyn T>
和Box<dyn S>
,但S
和T
都具有Clone
特性限制。 - 非公共Struct。即不完全内部可见的Struct。这将导致编译器错误,但在运行此宏时不会被捕获。这可能是不切实际的?
无法解决的问题
这些问题要么无法解决,要么非常不切实际。例如,解析所有文件并对给定软件包进行完全类型解析可能是不切实际的。这将是编译器在后续阶段的工作。
- 由其他宏修改或生成的Struct。
- 类型别名。例如,
type test = Option<String>
不会被检测为Option。当前对Option
字段的检查是对Option
标记的逐字检查。
依赖关系
~1.5MB
~37K SLoC