简体   繁体   English

是否可以将 `Keys<'_, K, V>` 迭代器作为 `&K` 的更通用的迭代器返回?

[英]Is it possible to return a `Keys<'_, K, V>` iterator as an more generic iterator of `&K`?

I would to write a method that returns self.hash_map.keys() while hiding from the caller the concrete type Keys<'_, K, V> .我会编写一个返回self.hash_map.keys()的方法,同时向调用者隐藏具体类型Keys<'_, K, V>

A downside of the Keys return type is that it exposes to the caller that the K elements are coming from a HashMap. Keys返回类型的一个缺点是它向调用者公开K元素来自 HashMap。 Intuitively, it seems as though it shouldn't be necessary to expose this information.直觉上,似乎没有必要公开这些信息。

  • What is the cheapest way (with respect to CPU/allocations) to return an iterator of key references?返回键引用的迭代器最便宜的方法是什么(相对于 CPU/分配)?
  • Can it be accomplished by some precise choice of return type?可以通过一些精确的返回类型选择来完成吗? Is some form of type-erasure possible?某种形式的类型擦除可能吗?
  • Or does it require an invocation in the function body?还是需要在 function 主体中调用? Is some transformation necessary?有必要进行一些改造吗?

Both of the options you speculated about are possible.您推测的两个选项都是可能的。

The simplest option is to use the impl type syntax, which is an “existential” type: “I am going to return a value which implements Iterator but I'm not telling you what the concrete type is”.最简单的选择是使用impl类型语法,它是一种“存在”类型:“我将返回一个实现Iterator的值,但我不会告诉你具体类型是什么”。 In this case, the compiler knows what the type is (so the compiled code is exactly the same as if it wasn't hidden), but the user of your method cannot rely on anything but the specified trait, so you aren't leaking implementation details.在这种情况下,编译器知道类型是什么(因此编译后的代码与未隐藏时完全相同),但您的方法的用户只能依赖指定的特征,因此您不会泄漏实施细节。

impl MyType {
    fn keys(&self) -> impl Iterator<Item = &MyKeyType> {
        self.hash_map.keys()
    }
}

(Note that this resembles but is not the same as dyn Iterator ; when you use dyn , you're using runtime dispatch and the same function can return different concrete types from different calls to it. With impl , the type is static, just hidden, and there is no overhead.) (请注意,这与dyn Iterator相似但不同;当您使用dyn时,您正在使用运行时调度,并且相同的 function 可以从不同的调用返回不同的具体类型。使用impl ,类型是 static,只是隐藏,并且没有开销。)

The disadvantage of this option is that the type is entirely unnameable;此选项的缺点是类型完全无法命名; for example, nobody can write a structure that holds your keys() iterator except by making it generic over all Iterator s.例如,没有人可以编写一个包含您的keys()迭代器的结构,除非将其设为对所有Iterator的通用。 (This is rarely a problem for iterators in particular, since iterator wrappers are usually generic anyway.) (这对于迭代器来说很少是个问题,因为迭代器包装器通常是通用的。)

Also, if your iterator implements any additional traits you want to allow the caller to use, like Debug or ExactSizeIterator , then you need to add them to the impl type or they won't be visible.此外,如果您的迭代器实现了您希望允许调用者使用的任何其他特征,例如DebugExactSizeIterator ,那么您需要将它们添加到impl类型中,否则它们将不可见。


Another option is to wrap the iterator in your own struct.另一种选择是将迭代器包装在您自己的结构中。 This allows you to hide the implementation type while still allowing callers to refer to it by name, so it's the most flexible.这允许您隐藏实现类型,同时仍允许调用者按名称引用它,因此它是最灵活的。 The disadvantage of this option is that you have to explicitly implement Iterator (and any other traits) for the wrapper:此选项的缺点是您必须为包装器显式实现Iterator (和任何其他特征):

impl MyType {
    fn keys(&self) -> MyKeyIterator<'_> {
        MyKeyIterator(self.hash_map.keys())
    }
}

#[derive(Clone, Debug)]
struct MyKeyIterator<'a>(Keys<'a, MyKeyType, MyValueType>);

impl<'a> Iterator for MyKeyIterator<'a> {
    type Item = &'a MyKeyType;

    fn next(&mut self) -> Option<&'a MyKeyType> {
        self.0.next()
    }
}

Rust Playground link with supporting code Rust 游乐场链接带支持代码

This wrapper should not add any performance cost (when compiled with optimization), except that by default the wrapper method will not be inlined if called from another crate.这个包装器不应该增加任何性能成本(当使用优化编译时),除了默认情况下如果从另一个箱子调用包装器方法将不会被内联。 If you're writing a library and this method is performance-sensitive, you can either enable link-time optimization (LTO) in the build, or add #[inline] to the next method (which enables cross-crate inlining).如果您正在编写库并且此方法对性能敏感,您可以在构建中启用链接时优化 (LTO),或将#[inline]添加到next方法(启用跨箱内联)。 Of course, don't do any such tweaking without checking whether it makes a difference to actual performance;当然,不要在不检查它是否对实际性能产生影响的情况下进行任何此类调整; otherwise you're just increasing compile time (and instruction cache thrashing).否则你只是增加编译时间(和指令缓存抖动)。

Can it be accomplished by some precise choice of return type?可以通过一些精确的返回类型选择来完成吗? Is some form of type-erasure possible?某种形式的类型擦除可能吗?

Yes!是的! You can return an impl Trait to indicate that you're returning a type that implements Trait but doesn't expose the concrete type:您可以返回一个impl Trait以表明您正在返回一个实现Trait但不公开具体类型的类型:

fn keys(&self) -> impl Iterator<Item = &K> {
    self.hash_map.keys()
}

See it working on the playground .看到它在操场上工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM