[英]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.