无法从函数返回 Iterator::Item。

huangapple go评论62阅读模式
英文:

Cannot return Iterator::Item from function

问题

这段代码的问题在于你尝试从一个Vec中移动出一个不实现Copy trait的类型。为了解决这个问题,你可以使用std::clone::Clone trait来复制元素而不是移动它们。以下是修改后的代码片段:

#[derive(EnumIter)]
pub enum Colors {
    Red,
    Blue,
    Purple
}

fn get_random_enum<T: Iterator>(iter: &mut T, rng: &mut Random) -> T::Item
where
    T::Item: Clone, // 添加此行以指示T::Item必须实现Clone trait
{
    let options = iter.collect::<Vec<_>>();
    let count = options.len();
    
    let idx = rng.rand_range(0, (count-1) as u32);
    let item = options[idx as usize].clone(); // 使用clone方法来复制元素

    item
}

fn main() {
    let rng = create_MyRandomNumberGenerator();
    let color = get_random_enum(&mut Colors::iter(), rng);
}

通过在get_random_enum函数中添加T::Item: Clone的约束,你告诉编译器要求T::Item类型必须实现Clone trait,从而可以使用clone()方法来复制元素而不是移动它们。这样,你的代码就不会再出现编译错误。

英文:

I'm writing a function to select a random variant from an enum with the help of the crate strum and its EnumIter derive macro:

#[derive(EnumIter)]
pub enum Colors {
	Red,
	Blue,
	Purple
}

fn get_random_enum&lt;T: Iterator&gt;(iter: &amp;mut T, rng: &amp;mut Random) -&gt; &lt;T as Iterator&gt;::Item {
	
	let options = iter.collect::&lt;Vec&lt;_&gt;&gt;();
	let count = options.len();
	
	let idx = rng.rand_range(0, (count-1) as u32);
	let item = options[idx as usize];

	return item;
}


fn main() {
    let rng = create_MyRandomNumberGenerator();
    let color = get_random_enum(&amp;mut Colors::iter(), rng);
}

This does not compile, as it states that:

error[E0507]: cannot move out of index of `Vec&lt;&lt;T as Iterator&gt;::Item&gt;`
  --&gt; stage/main.rs:15:13
   |
15 |     let item = options[idx as usize];
   |                ^^^^^^^^^^^^^^^^^^^^^ move occurs because value has type `&lt;U as Iterator&gt;::Item`, which does not implement the `Copy` trait

I'm quite new to Rust, but as I understand, &lt;U as Iterator&gt;::Item does not implement the Copy trait and that's the reason I cannot just return it.

Checking the generated code by the strum macro, I see that Item is just an alias of my Colors enum, but even if I add the Copy derive to my enum this still does not work.
I'm quite lost what's the way to implement this in Rust.

答案1

得分: 2

不同于C++,Rust的泛型是有原则的,这意味着它们必须始终对其泛型参数的任何可能实例进行类型检查。因此,不仅需要为具体的结构体/枚举实现Copy,还需要在泛型类型边界中要求它。即使我们返回了一个.clone()Item实例,仍然需要要求Item实现Clone特性。

解决方案:正如编译器提示的那样,我们需要使泛型函数要求迭代器的Item类型实现Copy

fn get_random_enum<T: Iterator<Item=I>, I: Copy>(iter: &mut T, rng: &mut Random) -> I {
    
    let options = iter.collect::<Vec<_>>();
    let count = options.len();
    
    let idx = rng.rand_range(0, (count-1) as u32);
    let item = options[idx as usize];

    return item;
}
英文:

Unlike in C++, Rust generics are principled, meaning that they need to always typecheck for any possible instantiation of their generic parameters. Therefore it does not suffice to implement Copy for your concrete struct/enum, but you also need to require it in the generic type bound. The same would even be necessary if we returned a .clone()d instance of the Item, we would then still need to require the Clone trait for any Item.

Solution: As the compiler hints at, we need the generic function to require the Item type of the iterator to implement Copy:

fn get_random_enum&lt;T: Iterator&lt;Item=I&gt;, I: Copy&gt;(iter: &amp;mut T, rng: &amp;mut Random) -&gt; I {
    
    let options = iter.collect::&lt;Vec&lt;_&gt;&gt;();
    let count = options.len();
    
    let idx = rng.rand_range(0, (count-1) as u32);
    let item = options[idx as usize];

    return item;
}

答案2

得分: 2

你可以通过这种方式从Vec中删除所需的项,这样就不必再引入更多的边界:

fn get_random_item<T: Iterator>(iter: &mut T, rng: &mut Random) -> <T as Iterator>::Item {
    let mut items = iter.collect::<Vec<_>>();
    let count = items.len();

    let idx = rng.rand_range(0, (count - 1));

    items.swap_remove(idx)
}

如果你的随机生成器实现了rand::Rng,你也可以直接使用IteratorRandom扩展特性 来直接选择其中的一个项:

use rand::seq::IteratorRandom;
fn get_random_enum<T: IntoEnumIterator, R: Rng>(rng: &mut R) -> T {
    T::iter().choose_stable(rng).unwrap()
}
英文:

You can remove the desired item from that Vec that way you don't have to introduce any more bounds:

fn get_random_item&lt;T: Iterator&gt;(iter: &amp;mut T, rng: &amp;mut Random) -&gt; &lt;T as Iterator&gt;::Item {
    let mut items = iter.collect::&lt;Vec&lt;_&gt;&gt;();
    let count = items.len();

    let idx = rng.rand_range(0, (count - 1));

    items.swap_remove(idx)
}

If your random generator implements rand::Rng you can also just use the IteratorRandom extension trait to directly choose on of the items:

use rand::seq::IteratorRandom;
fn get_random_enum&lt;T: IntoEnumIterator, R: Rng&gt;(rng: &amp;mut R) -&gt; T {
    T::iter().choose_stable(rng).unwrap()
}

huangapple
  • 本文由 发表于 2023年3月31日 21:34:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/75899154.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定