簡體   English   中英

計算結構 Rust 中“無”類型的字段數

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

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM