Muhammad Lukman Low
Muhammad Lukman Low

Reputation: 8533

Can't figure out return type of this Rust function that returns Iter?

I have this small Rust function:

pub fn factor(input_array: &mut [i32]) {
        let x = input_array
            .iter()
            .filter(|&x| x % 2 == 0);
        x
}

When I run this via cargo run I get this error:

Compiling gettingrusty v0.0.1 (file:///home/lowks/src/rust/gettingrusty)
src/functional.rs:22:9: 22:10 error: mismatched types:
 expected `()`,
    found `core::iter::Filter<core::slice::Iter<'_, i32>, [closure@src/functional.rs:21:21: 21:36]>`
(expected (),
    found struct `core::iter::Filter`) [E0308]
src/functional.rs:22         x
                             ^
src/functional.rs:22:9: 22:10 help: run `rustc --explain E0308` to see a detailed explanation
error: aborting due to previous error
Could not compile `gettingrusty`.

I tried a few return types such as slice::Iter<i32> and core::slice::Iter<i32> but seems like all of them are wrong. What should be the return type of my function?

Upvotes: 0

Views: 647

Answers (2)

kirelagin
kirelagin

Reputation: 13616

Your function returns a Filter object, so its actual return type is Filter<_, _> for some generic arguments. That’s fine, but chances are, you’ll want to hide all the implementation details from the type signature and just say that your function returns some iterator. Unfortunately, there is no (as of today) easy way to do this.

The pattern that seems to be rather common is to use a newtype wrapper. The problem with this is that writing the wrapper is a little bit more difficult than one might expect, e.g. one will have to deal with lifetimes explicitly.

Here is a complete example:

use std::iter::Filter;
use std::slice::Iter;

struct FactorResult<'a, T: 'a>(Filter<Iter<'a, T>, fn(&&T) -> bool>);

impl<'a, T> Iterator for FactorResult<'a, T> {
    type Item = &'a T;
    fn next(&mut self) -> Option<&'a T> { self.0.next() }
    fn size_hint(&self) -> (usize, Option<usize>) { self.0.size_hint() }
}

fn factor(input_array: &[i32]) -> FactorResult<i32> {
    fn even(x : &&i32) -> bool { **x % 2 == 0 }

    FactorResult(input_array.iter().filter(even))
}

fn main () {
    for x in factor(&[1,2,3,4]) {
        println!("{}", x);
    }
}
  • The factor function returns a FactorResult which is just a wrapper that hides the actual underlying type.
  • The only thing the user knows about FactorResult is that it is an Iterator. The implementation of the trait is trivial, but I had to spell it out.
  • I had to replace the closure with the function. This is because here Rust does not perform any allocations, so it needs to know the size of FactorResult<T>, but the type of the closure is anonymous so there is no way to refer to it. One could use a closure but the whole thing would have to be boxed in this case.

Upvotes: 2

DK.
DK.

Reputation: 59065

Under normal circumstances, you could just copy+paste the found part of the error message. There are two problems with that in this particular case.

First, core isn't directly accessible. Various items are exposed by the standard library libstd, but are actually defined by libcore. Basically, the standard library is the public interface you are meant to use to access these items, but the compiler doesn't know that. Typically, you work around this by just replacing core::* with std::*.

The second problem is that the type includes a closure, and closures cannot be named. The simplest solution is to just not use a closure at all; you aren't capturing anything, anyway.

Doing that and just fixing the compile errors as they come up leads to:

pub fn factor(input_array: &mut [i32])
-> std::iter::Filter<std::slice::Iter<i32>, fn(&&i32) -> bool> {
    fn even(x: &&i32) -> bool { **x % 2 == 0 }
    let x = input_array
        .iter()
        .filter(even as for<'r> fn(&'r &_) -> _);
    x
}

Upvotes: 3

Related Questions