1

考虑这个伪伪代码。keyarg我对下面的论点有很多困难,

type MapSet<K,V> = HashMap<K,HashSet<V>>;

fn contract<K,V,V1>(left: &MapSet<K,V>, right: &MapSet<V,V1>, 
                    out: &mut MapSet<K,V1>,
                    keyarg: Option(__something_iterable__) {

    let keys = match keyarg {
        Some(keys) => {
            keys
        },
        None => {
           left.keys()
        },
    }

    for &k in keys {
        // ... do stuff ...
    }
}

该功能有时会这样使用,

let x: MapSet<u32,String>;
let y: MapSet<String,u32>;
let out: MapSet<u32,u32>;
let kk: HashSet<u32>;

// ... snip ...

contract(x,y,out,kk);

而且,在其他类似的时候,

let x: MapSet<u32,String>;
let y: MapSet<String,u32>;
let out: MapSet<u32,u32>;
contract(x,y,out,None);

换句话说,我有时使用keyarg参数来传递Option带有对包含我想要迭代的键的 a 的引用,HashSet而在其他时候我想迭代包含在left参数中的所有键,所以keyarg变成了简单的None.

但是,到目前为止,我总是遇到一个问题,match它抱怨 None 导致Keys对象和 Some branch 到 HashSet (类型不匹配错误)。

我的问题是如何定义keyarg论点,以便分支match彼此兼容。也就是说,我想表达一个事实,即变量keys只是一个可以迭代到编译器的东西。

4

1 回答 1

1

除非keyarg总是匹配你的MapSet迭代器,而在你的伪代码中它不会,你需要keys成为一个特征对象。for 的迭代器MapSet<K,V>具有 trait Iterator<Item = &K>,因此您可以将其匹配为Box如下形式:

let keys: Box<dyn Iterator<Item = &K>> = match keyarg {
    Some(keys) => Box::new(...),
    None => Box::new(left.keys()),
}

至于确定keyarg应该是什么,通用的方法是接受任何匹配的迭代,如下所示:

fn contract<'a, K, V, V1, I: IntoIterator<Item = &'a K>>(
                       // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    left: &'a MapSet<K, V>,
    right: &MapSet<V, V1>,
    out: &mut MapSet<K, V1>,
    keyarg: Option<I>,
) {    
    let keys: Box<dyn Iterator<Item = &K>> = match keyarg {
        Some(keys) => Box::new(keys.into_iter()),
        None => Box::new(left.keys()),
    };

    // ...
}

这对于传递 a 非常符合人体工程学,HashSet但会导致None案例出现问题:

// This works
contract(&x, &y, &mut out, Some(&kk));

// This fails with error: "type annotations needed, cannot infer type for type parameter `I`"
contract(&x, &y, &mut out, None); 

因此,您必须使用一些不理想的None虚拟迭代器类型(例如Box<...>或其他)进行注释。std::iter::Empty

相反,您可以直接制作keyarg盒装迭代器:

fn contract_1<'a, K, V, V1>(
    left: &'a MapSet<K, V>,
    right: &MapSet<V, V1>,
    out: &mut MapSet<K, V1>,
    keyarg: Option<Box<dyn Iterator<Item = &'a K> + 'a>>,
                // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
) {
    let keys: Box<dyn Iterator<Item = &K>> = match keyarg {
        Some(keys) => keys,
        None => Box::new(left.keys()),
    };

    // ...
}

可以这样使用:

contract(&x, &y, &mut out, Some(Box::new(kk.iter())));
contract(&x, &y, &mut out, None);
于 2021-01-22T18:10:27.483 回答