Daniel Fath
Daniel Fath

Reputation: 18089

Figuring out return type of closure

I'm having troubles figuring out the type signature of the fn filter function in following example.

The Node and Descendant definition is just there for syntax . It's not meant to do anything!

use std::iter::Filter;

#[derive(Clone)]
pub struct Node<'a> {
   s: &'a str,
}

pub struct Descendants<'a>{
    iter: Node<'a>
}

impl<'a> Iterator for Descendants<'a> {
    type Item = Node<'a>;

    fn next(&mut self) -> Option<Node<'a>> {
        Some(Node {s: self.iter.s})
    }
}

impl<'a> Node<'a> {
    pub fn descendants(&self) -> Descendants<'a> {
        Descendants{ iter: Node{s: self.s} }  
    }

    pub fn filter(&self, criteria: &str) -> Filter<Descendants<'a>, fn(&'a Node<'a>)->bool > {
        self.descendants()
            .filter(|node| node.s == "meh")
    }
}

fn main() {
    let doc = Node{s: "str"};

}

(Playground link)

The error I get is following:

<anon>:27:28: 27:34 error: the type of this value must be known in this context
<anon>:27             .filter(|node| node.s == "meh")
                                     ^~~~~~
<anon>:27:21: 27:43 error: mismatched types:
 expected `fn(&Node<'_>) -> bool`,
    found `[closure <anon>:27:21: 27:43]`
(expected fn pointer,
    found closure) [E0308]
<anon>:27             .filter(|node| node.s == "meh")
                              ^~~~~~~~~~~~~~~~~~~~~~
<anon>:27:14: 27:44 error: type mismatch: the type `fn(&Node<'_>) -> bool` implements the trait `core::ops::FnMut<(&Node<'_>,)>`, but the trait `for<'r> core::ops::FnMut<(&'r Node<'_>,)>` is required (expected concrete lifetime, found bound lifetime parameter ) [E0281]
<anon>:27             .filter(|node| node.s == "meh")
                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<anon>:27:14: 27:44 error: type mismatch resolving `for<'r> <fn(&Node<'_>) -> bool as core::ops::FnOnce<(&'r Node<'_>,)>>::Output == bool`:
 expected bound lifetime parameter ,
    found concrete lifetime [E0271]
<anon>:27             .filter(|node| node.s == "meh")
                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to 4 previous errors
playpen: application terminated with error code 101

When I as per this question Correct way to return an Iterator? I tried to replace pub fn filter(&self, criteria: &str) -> Filter<Descendants<'a>, fn(&'a Node<'a>)->bool > with pub fn filter(&self, criteria: &str) -> () I get

<anon>:26:9: 27:44 error: mismatched types:
 expected `()`,
    found `core::iter::Filter<Descendants<'_>, [closure <anon>:27:21: 27:43]>`

What I'm supposed to replace closure with?

Alternatively, if it's too hard and finicky to return a Filter, how do I write the Wrapper for fn filter() return type?

Upvotes: 3

Views: 830

Answers (1)

Vladimir Matveev
Vladimir Matveev

Reputation: 127911

I clearly remember that this was answered before a few times (I even wrote about it in an answer a few minutes before) but I can't find a link now, so here it goes.

The problem with your code is that you use a closure as filter() argument:

.filter(|node| node.s == "meh")

Unboxed closures in Rust are implemented as anonymous types which, naturally, can't be named, so there is no way to write a signature of a function which returns an iterator which uses a closure. That's what the error message you're getting is about:

 expected `fn(&Node<'_>) -> bool`,
    found `[closure <anon>:27:21: 27:43]`
(expected fn pointer,
    found closure) [E0308]

There are several ways around this, one of them is to use trait objects:

pub fn filter<'b>(&'b self, criteria: &'b str) -> Box<Iterator<Item=Node<'a>+'b>>
{
    Box::new(self.descendants().filter(move |node| node.s == criteria))
}

Given that your closure has a non-empty environment, this is the only way for your code to work. If your closure didn't capture anything, you could use a static function whose type can be written out:

pub fn filter(&self) -> Filter<Descendants<'a>, fn(&Node<'a>) -> bool> {
    fn filter_fn<'b>(node: &Node<'b>) -> bool {
        node.s == "meh"
    }
    self.descendants().filter(filter_fn)
}

Upvotes: 7

Related Questions