11 个版本 (6 个破坏性更新)
0.7.0 | 2024 年 4 月 19 日 |
---|---|
0.6.0 | 2024 年 3 月 9 日 |
0.5.0 | 2024 年 3 月 7 日 |
0.4.3 | 2024 年 2 月 18 日 |
0.1.0 | 2024 年 1 月 15 日 |
#158 in 构建工具
每月 1,062 次下载
56KB
989 行
特质变量
为 Rust 使特质定义中的变量字段成为可行。
具体来说,此软件包通过过程宏以及结构体,在特质定义中启用变量字段的用法,模拟了像 C++ 和 Python 这样的语言中发现的继承。此功能在不修改 Rust 的核心语言特性的情况下实现,提供了一种解决方案,直到类似的特性可能正式引入 Rust。
用法
要使用此软件包,将其添加到您的 Cargo.toml
[dependencies]
trait_variable = "*"
然后,按照以下方式将宏集成到您的 Rust 代码中
/*↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓trait definition↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/
use trait_variable::{trait_var, trait_variable};
trait_variable! {
pub(crate) trait MyTrait { // feel free to add `pub` when needed
// 1.put the variable fields definition at the VERY TOP of the target trait before any vaild trait item
x: i32;
pub y: bool;
// 2.the order of the original valid trait items doesn't matter
fn print_x(&self){
println!("x: `{}`", self.x);
}
fn print_y(&self){
println!("y: `{}`", self.y);
}
fn print_all(&self);
}
}
/*↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑trait definition↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑*/
/*↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓struct definition↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/
// way1: use the attribute macro to expand the struct (Recommended)
#[trait_var(MyTrait)]
pub struct MyStruct {
a: i32,
pub b: String,
}
// way2: use the hidden declarative macro to expand the struct (Not recommended)
// MyTrait_for_struct! {
// pub struct MyStruct { // feel free to add `pub` when needed
// // feel free to add any fields as usual or leave it empty
// a: i32,
// pub b: String,
// }
// }
/*↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑struct definition↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑*/
/*↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓struct impl↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/
// the struct basic implementation
impl MyStruct {
pub fn new(a: i32, b: String, x: i32, y: bool) -> Self {
Self { a, b, x, y }
}
}
// the trait implementation
impl MyTrait for MyStruct {
fn print_all(&self) {
println!("a: `{}`", self.a);
println!("b: `{}`", self.b);
self.print_x();
println!("y: `{}`", self.y);
}
}
/*↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑struct impl↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑*/
#[test]
fn test() {
let s = MyStruct::new(1, "hello".into(), -2, true);
s.print_all();
assert_eq!(s.x, -2); // if not in a unit test, then `self.x`` is not accessible, since it is private
assert_eq!(s.y, true); // s.x is private
}
技术细节
上述示例演示了如何定义具有变量字段的特质并为结构体实现它。`trait_variable!` 宏简化了在特质中包含变量字段的过程,而 `#[trait_var]` 属性宏自动处理实现细节,允许结构体使用这些字段就像它们自己的字段一样。有关完整示例,请参阅 基本示例(还有更多全面且实用的示例,一个更复杂的示例,包含泛型,您可以参考)
具体来说,`x` 和 `y` 是特质 `MyTrait` 的变量字段,`MyStruct` 是实现了 `MyTrait` 的结构体。`trait_variable!` 功能宏会自动将特质变量转移到以 `_` 作为前缀的新父特质(在本例中为 `_MyTrait`)。以下是如何为 `_MyTrait` 生成默认实现的示例
pub(crate) trait _MyTrait {
fn _x(&self) -> &i32;
fn _x_mut(&self) -> &mut i32;
fn _y(&self) -> &bool;
fn _y_mut(&self) -> &mut bool;
}
pub(crate) trait MyTrait: _MyTrait {
// 1. the trait variable is erased here
// 2. the original trait definition is kept here
// ...
}
通过这种方式,在使用特征变量的基础构建过程中,不会违反任何规则。可以很容易地猜测,任何以 self.x
作为特征变量标识符的特征函数体将被转换为 self._x()
或 self._x_mut()
。
此外,#[trait_var(MyTrait)]
属性宏首先将目标结构体扩展为上述块 way2:
中所述的,由隐藏声明宏(同样由 trait_variable!
生成)包裹的形式,同时包含一个指向隐藏特征路径的导入语句。然后这个隐藏声明宏将进一步为 MyStruct
生成 _MyTrait
(不是 MyTrait
)的默认实现。
use crate::PathToMyTrait; // if the trait is defined in another file
impl _MyTrait for MyStruct {
fn _x(&self) -> &i32 {
&self.x
}
fn _x_mut(&self) -> &mut i32 {
&mut self.x
}
fn _y(&self) -> &bool {
&self.y
}
fn _y_mut(&self) -> &mut bool {
&mut self.y
}
}
最后,x
和 y
可以像 MyStruct
的字段一样访问,好像它们是 MyStruct
的字段一样!没有其他字段代码!干杯~ ^_^;可选地,可以像通常一样为 MyTrait
中定义的方法实现 MyStruct
。(注意:如果结构体定义在定义特征的文件之外的文件中,需要在定义结构体的文件开头添加 use <trait_module>::<hidden_parent_trait_name>
-- 在这种情况下,<hidden_parent_trait_name>
是 _MyTrait
。)
注意:在整个实现过程中,x
和 y
仍然是 MyTrait
的字段,但从技术上讲是 MyStruct
的字段。也就是说,这里没有任何继承机制,因为 x
和 y
的状态/操作仅在实现的结构体中处理--是的,结构体仍然像通常一样通过接口(特征)定义,而不是通过继承。
局限性
在 trait_variable
宏内部的代码可能无法从 Rust 扩展(如 Rust Analyzer)获得全面的智能提示和 lint 支持。这是因为大多数 Rust 扩展目前难以有效地处理宏内部的标识符。因此,当使用此 crate 时,您可能无法访问完整的代码补全、重构、转到定义和其他智能感知功能。这是一个已知的局限性,未来版本可能会得到改进(如果技术上可行)。
此外,由于声明性宏的一些问题,目前,如果两个宏在同一文件中使用,则 trait_variable!
宏应放在 #[trait_var(MyTrait)]
属性宏之前,否则隐藏特征 _MyTrait
将无法找到。这个问题将在未来得到解决。
要求
- 需要 Rust 版本 2021 或更高。
- 该仓库已经使用Rust版本1.77.1进行了测试,但未指定最低兼容版本。
贡献
欢迎贡献。请随意提交pull请求或在GitHub仓库上打开问题。
待办事项列表
- [] 在结构体定义中处理trait bound(where子句);
- [] 在结构体定义中使用生命周期;
- [] 在结构体定义中使用具有数组字段的常量泛型;
- [] 检查函数
replace_self_field
的参数必要性; - [] 在trait定义中使用GAT、异步方法等;
- [] 尝试让智能智能感应扩展(如
Rust Analyzer
)支持宏内部的标识符(可能不可能)。
许可证
此仓库可以在以下两种许可证下使用:
- Apache许可证2.0版本 (http://www.apache.org/licenses/LICENSE-2.0)
- MIT许可证 (http://opensource.org/licenses/MIT)
选择最适合您需求的许可证。
依赖关系
~2–11MB
~111K SLoC