107

I'd like to write a function that accepts an iterator and returns the results of some operations on it. Specifically, I'm trying to iterate over the values of a HashMap:

use std::collections::HashMap;

fn find_min<'a>(vals: Iterator<Item=&'a u32>) -> Option<&'a u32> {
    vals.min()
}

fn main() {
    let mut map = HashMap::new();
    map.insert("zero", 0u32);
    map.insert("one", 1u32);
    println!("Min value {:?}", find_min(map.values()));
}

But alas:

error: the `min` method cannot be invoked on a trait object
 --> src/main.rs:4:10
  |
4 |     vals.min()
  |          ^^^

error[E0277]: the trait bound `std::iter::Iterator<Item=&'a u32> + 'static: std::marker::Sized` is not satisfied
 --> src/main.rs:3:17
  |
3 | fn find_min<'a>(vals: Iterator<Item = &'a u32>) -> Option<&'a u32> {
  |                 ^^^^ `std::iter::Iterator<Item=&'a u32> + 'static` does not have a constant size known at compile-time
  |
  = help: the trait `std::marker::Sized` is not implemented for `std::iter::Iterator<Item=&'a u32> + 'static`
  = note: all local variables must have a statically known size

error[E0308]: mismatched types
  --> src/main.rs:11:41
   |
11 |     println!("Min value {:?}", find_min(map.values()));
   |                                         ^^^^^^^^^^^^ expected trait std::iter::Iterator, found struct `std::collections::hash_map::Values`
   |
   = note: expected type `std::iter::Iterator<Item=&u32> + 'static`
              found type `std::collections::hash_map::Values<'_, &str, u32>`

I get the same error if I try to pass by reference; if I use a Box, I get lifetime errors.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Doctor J
  • 5,974
  • 5
  • 44
  • 40
  • 3
    Many use cases would benefit from asking a broader question: "How to write a Rust function that takes an **iterable**?" By iterable, I mean something that can be iterated over. (This is broader than an iterator.) As mentioned [in this answer](https://stackoverflow.com/a/34969944/109618), to do that, use `IntoIterator`, because any type that implements `IntoIterator` is iterable. – David J. Aug 23 '21 at 18:44

3 Answers3

99

You want to use generics here:

fn find_min<'a, I>(vals: I) -> Option<&'a u32>
where
    I: Iterator<Item = &'a u32>,
{
    vals.min()
}

Traits can be used in two ways: as bounds on type parameters and as trait objects. The book The Rust Programming Language has a chapter on traits and a chapter on trait objects that explain these two use cases.

Additionally, you often want to take something that implements IntoIterator as this can make the code calling your function nicer:

fn find_min<'a, I>(vals: I) -> Option<&'a u32>
where
    I: IntoIterator<Item = &'a u32>,
{
    vals.into_iter().min()
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Francis Gagné
  • 60,274
  • 7
  • 180
  • 155
  • I was so close. To expound, the difference with generics is static dispatch, i.e., Rust is creating a version of this function for each concrete type I call it with? – Doctor J Jan 23 '16 at 22:59
  • 2
    Correct. This way, the compiler knows the type of the iterator, therefore it knows its size, so it knows how much memory it needs to reserve. Also, a type like `Iterator` cannot be used alone; it can only be used behind a pointer. – Francis Gagné Jan 23 '16 at 23:49
  • 25
    Note that while requiring `I` to implement `Iterator` is absolutely correct if you want to pass arbitrary iterators into the function, the more generic way would be to require `I` to implement [`IntoIterator`](http://doc.rust-lang.org/std/iter/trait.IntoIterator.html). It allows you to pass iterators too but you will be also able to pass anything which can be *converted* into an iterator, without the need to call conversion methods explicitly. I'd say this is *the* idiomatic approach to consume iterators and iterables. – Vladimir Matveev Jan 24 '16 at 10:35
  • @VladimirMatveev, can you elaborate a bit on that, please? I'm trying to get something like the following working with your advice... https://gist.github.com/anonymous/0c1db25a9b38a2fc95f44bec34ebd46a – burfl Sep 09 '16 at 00:43
  • @burfl: Your function should be returning `u64`, not `T`. – Francis Gagné Sep 09 '16 at 00:54
  • @FrancisGagné, holy cow, that was simple! Thank you so much! – burfl Sep 09 '16 at 00:55
  • 2
    With `foo: impl Type` this is much more easy now – Boiethios Oct 19 '18 at 15:38
  • " require I to implement IntoIterator"; but what if you want to pass something that does not use `into_iter` to return an iterator? For example, if I wanted to iterate over either vector elements (`my_vec.iter()`) OR a file handle (`fs::File::open("foo.txt").unwrap().lines()`), etc.. Does `IntoIterator` still apply here? – user5359531 Aug 20 '19 at 00:19
  • @user5359531 Yes, all iterators also implement `IntoIterator` thanks to a blanket impl (`impl IntoIterator for I where I: Iterator`; `into_iter` just returns `self`). – Francis Gagné Aug 20 '19 at 16:17
46

Since Rust 1.26 impl Trait are available. Here is a more compact version using impl Trait.

use std::collections::HashMap;

fn find_min<'a>(vals: impl Iterator<Item = &'a u32>) -> Option<&'a u32> {
    vals.min()
}

fn main() {
    let mut map = HashMap::new();
    map.insert("zero", 0u32);
    map.insert("one", 1u32);
    println!("Min value {:?}", find_min(map.values()));
}

playground

Fuji
  • 28,214
  • 2
  • 27
  • 29
  • 4
    Is there a reason why this wouldn't be better than the alternatives, or is it just more recent? – TimY Feb 08 '21 at 18:14
  • 1
    In Rust 1.26 impl Trait were added to the language. This a less verbose way to implement the same code – Fuji Feb 09 '21 at 12:15
9

This behaviour is a little unintuitive from those with a Python background rather than, say, a C++ background, so let me clarify a little.

In Rust, values are conceptually stored inside the name that binds them. Thus, if you write

let mut x = Foo { t: 10 };
let mut y = x;
x.t = 999;

y.t will still be 10.

So when you write

let x: Iterator<Item=&'a u32>;

(or the same in the function parameter list), Rust needs to allocate enough space for any value of type Iterator<Item=&'a u32>. Even if this was possible, it wouldn't be efficient.

So what Rust does instead is offer you the option to

  • Put the value on the heap, eg. with Box, which gives Python-style semantics. Then you can take generically with &mut Iterator<Item=&'a u32>.

  • Specialize each function invocation for each possible type to satisfy the bound. This is more flexible, since a trait reference is a possible specialization, and gives the compiler more opportunities for specialization, but means you can't have dynamic dispatch (where the type can vary dependent on runtime parameters).

Veedrac
  • 58,273
  • 15
  • 112
  • 169
  • 4
    The example here wouldn't compile, the let mut y = x assignment moves x, so line 3 cannot use x anymore. (unless you specify that Foo is Copy, but that gives a pretty good hint already that it won't behave like you would expect in scripting languages) – K. Norbert May 17 '20 at 09:58
  • 1
    Regardless of the semantics of "does it change the code, or does it change what you are allowed to do", the example code above is still invalid, and does not compile. – K. Norbert May 18 '20 at 10:01
  • 1
    I would assume it's non-copy because that's the default for structs, and there is no mention of it being Copy in your answer. – K. Norbert May 18 '20 at 10:12
  • 1
    The issue is that the code you are using to reason with is invalid, without some arbitrary assumptions that beginners might not have. – K. Norbert May 18 '20 at 21:03
  • 1
    Agreed - the code here is broken and will teach people incorrect things about Rust. Please fix! – ChrisPhoenix Jun 20 '21 at 20:18
  • @ChrisPhoenix The code is not broken and I maintain that emphasizing the difference between Copy and move is extremely misleading, and largely illusory, in this context. – Veedrac Jun 20 '21 at 22:14
  • Teaching, or even implying too strongly, that Copy changes the behaviour of the code is factually incorrect and based around an invalid model of Rust that I imagine stems from C++. The more you play into that myth, the more it spreads. – Veedrac Jun 20 '21 at 22:19
  • I copy-and-pasted your code into a .rs file adding "struct Foo { t: i32, }". It did not compile. y does not need to be mutable, and "x.t = 999;" gets a " | ^^^^^^^^^ value partially assigned here after move" because Foo "does not implement the `Copy` trait." Why do you say code that doesn't compile is not broken? When I add "#[derive(Clone, Copy)]" to Foo (and remove the "mut" on y) it compiles and indeed y.t is 10. – ChrisPhoenix Jun 21 '21 at 08:06
  • For what it's worth, I loathe C++ and have never really learned it; I've been an embedded software engineer for decades and I know the difference between a data-copy and a reference. So does the Rust compiler. It's not that Copy "changes the behavior of the code" - it's that Copy allows "let y = x; x.t=999;" to compile at all. – ChrisPhoenix Jun 21 '21 at 08:13
  • @ChrisPhoenix That's like saying “I copy-and-pasted your code into a .rs file adding "struct Foo {}". It did not compile. Why do you say code that doesn't compile is not broken?” I never specified that `Foo` *shouldn't* be marked `Copy`, I just don't want to draw attention to it because it's independent of the behaviour I'm explaining, just like I didn't draw attention to the member `t` or its type. You can't really understand why certain things need to be marked `Copy` until you understand what assignment does. – Veedrac Jun 22 '21 at 17:24