[英]Calculate number of fields that are of type "None" in a struct Rust
我正在研究一个宏,它计算结构上在运行时类型为 None 的字段数。 我有一个计算结构字段数的实现。 源代码如下。
use std::rc::Rc;
use std::cell::Cell;
macro_rules! generate {
($name:ident {$($field:ident : $t:ty),+}) => {
struct $name { $($field: $t),+ }
impl $name {
fn field_count(&self) -> usize {
generate!(@count $($field),+)
}
}
};
(@count $t1:tt, $($t:tt),+) => { 1 + generate!(@count $($t),+) };
(@count $t:tt) => { 1 };
}
generate! { test1 { num: i32, s: Option<String> }}
generate! { test2 { num: i32, s: String }}
fn main() {
let s = test1 { num: 0, s: None };
println!("{}", s.field_count());
let s = test2 { num: 0, s: String::new() };
println!("{}", s.field_count());
}
这个实现的问题是它在编译时返回结构中的字段总数,所以对于 test1 和 test1,答案总是 2。我正在寻找一种方法来修改宏来计算类型为“无”的字段在结构中。
要区分None
,我们首先需要区分Option
。 这有点难。
有两种方法可以做到这一点:语法或通过类型系统。 从语法上讲,宏将识别具有Option<T>
类型的字段(即Option
<
type >
)。 但是,这需要 TT 咀嚼,将迫使您自己解析ty
片段类型(您不能依赖它,因为您以后将无法检查其组成的令牌),并且如果用户无法使用它作为奖励将 type-alias Option
。
类型系统方法更健壮。 这个想法是创建一些特征,为任何类型实现,使用Option<T>
将返回1
如果它是Some
和其他类型将始终返回1
的方法。 这听起来像是一个完美的 专业化用例( 游乐场):
#![feature(min_specialization)]
pub trait OneIfSome {
fn one_if_some(&self) -> usize;
}
impl<T> OneIfSome for T {
default fn one_if_some(&self) -> usize { 1 }
}
impl<T> OneIfSome for Option<T> {
fn one_if_some(&self) -> usize { self.is_some() as usize }
}
macro_rules! generate {
($name:ident {$($field:ident : $t:ty),+}) => {
struct $name { $($field: $t),+ }
impl $name {
fn field_count(&self) -> usize {
generate!(@count self $($field,)*)
}
}
};
(@count $self:ident $first_field:ident, $($rest:ident,)*) => {
$crate::OneIfSome::one_if_some(&$self.$first_field)
+ generate!(@count $self $($rest,)*)
};
(@count $self:ident) => { 0 };
}
不幸的是,专业化是不稳定的,甚至没有明确的稳定之路。 我们可以用稳定的东西代替它吗?
事实证明,是的! @dtolnay 发明了一种称为“自动引用专业化”的技术,该技术完全适用于此类情况(宏内的专业化)。 我不会在这里解释它是如何工作的(链接的文章是一个很好的阅读),但这里是你如何将它应用到你的案例( 操场):
pub trait OneIfSomeGeneral {
fn one_if_some(&self) -> usize;
}
impl<T> OneIfSomeGeneral for &'_ T {
fn one_if_some(&self) -> usize { 1 }
}
pub trait OneIfSomeOption {
fn one_if_some(&self) -> usize;
}
impl<T> OneIfSomeOption for Option<T> {
fn one_if_some(&self) -> usize { self.is_some() as usize }
}
macro_rules! generate {
($name:ident {$($field:ident : $t:ty),+}) => {
struct $name { $($field: $t),+ }
impl $name {
fn field_count(&self) -> usize {
#[allow(unused_imports)]
use $crate::{OneIfSomeGeneral, OneIfSomeOption};
generate!(@count self $($field,)*)
}
}
};
(@count $self:ident $first_field:ident, $($rest:ident,)*) => {
(&$self.$first_field).one_if_some()
+ generate!(@count $self $($rest,)*)
};
(@count $self:ident) => { 0 };
}
(注意:上面的代码有一个问题:如果字段类型有一个名为one_if_some()
的方法,它会优先。所以选择一个明确的名称( __my_generate_macro__one_if_some()
,也许?))。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.