簡體   English   中英

Rust:如何將 `iter().map()` 或 `iter().enumarate()` 分配給同一個變量

[英]Rust: how to assign `iter().map()` or `iter().enumarate()` to same variable

struct A {...whatever...};
const MY_CONST_USIZE:usize = 127;


// somewhere in function

// vec1_of_A:Vec<A> vec2_of_A_refs:Vec<&A> have values from different data sources and have different inside_item types
let my_iterator;
if my_rand_condition() { // my_rand_condition is random and compiles for sake of simplicity
    my_iterator = vec1_of_A.iter().map(|x| (MY_CONST_USIZE, &x)); // Map<Iter<Vec<A>>>
} else {
    my_iterator = vec2_of_A_refs.iter().enumerate(); // Enumerate<Iter<Vec<&A>>>
}

如何使這段代碼編譯?

最后(基於條件)我希望能夠從兩個輸入構建迭代器,但我不知道如何將這些MapEnumerate類型集成到單個變量中,而不調用collect()將迭代器具體化為Vec或者它可能是反對的函數式編程原則

歡迎閱讀材料

vec_of_A的情況下,首先您需要在 map function 中將&x替換為x 您擁有的代碼永遠不會編譯,因為映射閉包試圖返回對其參數之一的引用,這在 Rust 中是不允許的。 為了使類型匹配,您需要將vec2_of_A_refs案例中的&&A取消引用到&A而不是嘗試添加對另一個的引用。

此外, -127usize的無效值,因此您需要選擇一個有效值,或使用與usize不同的類型。

修復了這些,現在您需要某種類型的動態調度。 最簡單的方法是裝箱成Box<dyn Iterator>

這是一個完整的例子:

#![allow(unused)]
#![allow(non_snake_case)]

struct A;
// Fixed to be a valid usize.
const MY_CONST_USIZE: usize = usize::MAX;

fn my_rand_condition() -> bool { todo!(); }

fn example() {
    let vec1_of_A: Vec<A> = vec![];
    let vec2_of_A_refs: Vec<&A> = vec![];
    
    let my_iterator: Box<dyn Iterator<Item=(usize, &A)>>;
    if my_rand_condition() {
        // Fixed to return x instead of &x
        my_iterator = Box::new(vec1_of_A.iter().map(|x| (MY_CONST_USIZE, x)));
    } else {
        // Added map to deref &&A to &A to make the types match
        my_iterator = Box::new(vec2_of_A_refs.iter().map(|x| *x).enumerate());
    }
    
    for item in my_iterator {
        // ...
    }
}

游樂場

除了盒裝特征 object,您還可以使用either一板條箱中的Either類型。 這是一個具有LeftRight變體的枚舉,但是如果 left 和 right 類型也都這樣做,則Either類型本身實現Iterator ,並且Item關聯類型具有相同的類型。 例如:

#![allow(unused)]
#![allow(non_snake_case)]

use either::Either;

struct A;
const MY_CONST_USIZE: usize = usize::MAX;

fn my_rand_condition() -> bool { todo!(); }

fn example() {
    let vec1_of_A: Vec<A> = vec![];
    let vec2_of_A_refs: Vec<&A> = vec![];
    
    let my_iterator;
    if my_rand_condition() {
        my_iterator = Either::Left(vec1_of_A.iter().map(|x| (MY_CONST_USIZE, x)));
    } else {
        my_iterator = Either::Right(vec2_of_A_refs.iter().map(|x| *x).enumerate());
    }
    
    for item in my_iterator {
        // ...
    }
}

游樂場

為什么你會選擇一種方法而不是另一種?

Either方法的優點:

  • 它不需要堆分配來存儲迭代器。
  • 它通過match實現動態調度,這可能(但不保證)比通過 vtable 查找的動態調度更快。

盒裝特征 object 方法的優點:

  • 它不依賴於任何外部 crate。
  • 它很容易擴展到許多不同類型的迭代器; 如果有兩種以上的類型, Either方法很快就會變得笨拙。

您可以使用Box ed trait object 來執行此操作,如下所示:

let my_iterator: Box<dyn Iterator<Item = _>> = if my_rand_condition() {
    Box::new(vec1_of_A.iter().map(|x| (MY_CONST_USIZE, x)))
} else {
    Box::new(vec2_of_A_refs.iter().enumerate().map(|(i, x)| (i, *x)))
};

不過,我認為這通常不是一個好主意。 需要注意的幾點:

  • 使用 trait 對象意味着這里的類型必須動態解析。 這增加了很多開銷。
  • vec1的迭代器的map方法中的閉包無法引用其 arguments。 相反,必須將第二個map添加到vec2的迭代器中。 這樣做的效果是無論如何都將復制所有項目。 如果你這樣做,為什么不collect() 創建Vec或您選擇的任何內容的開銷應小於動態分辨率的開銷。
  • 有點迂腐,但請記住if語句是 Rust 中的表達式,因此可以像我上面所做的那樣更清晰地表達賦值。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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