Closure syntax in reference manual - example and syntax appear not to match


#1

http://doc.rust-lang.org/reference.html#lambda-expressions at 8.1.10 gives the syntax for closures. This is:

closure_type := [ ‘unsafe’ ] [ ‘<’ lifetime-list ‘>’ ] ‘|’ arg-list ‘|’ [ ‘:’ bound-list ] [ ‘->’ type ] lifetime-list := lifetime | lifetime ‘,’ lifetime-list arg-list := ident ‘:’ type | ident ‘:’ type ‘,’ arg-list bound-list := bound | bound ‘+’ bound-list bound := path | lifetime

“ident” is just a Rust identifier. (The formal definition is “any nonempty Unicode string of the following form: The first character has property XID_start The remaining characters have property XID_continue that does not occur in the set of keywords.” XID_start is defined by http://unicode.org/reports/tr31/ and does not contain “&”)

The following example in the reference manual has

let closure_no_args = |&:expressionless: println!(“captured_var={}”, captured_var);

How did & get into this in the slot for “ident”? The syntax says that the only thing allowed between the | | pair is a sequence of “ident : type”. Is this a language change that didn’t make it into the manual?

On a related note, all the examples for closures as arguments show them being passed to “higher order”, or generic, functions, with type arguments. Is that required for closures? I gather that a closure and a function parameter are not type-compatible, but the reference manual is not clear on that.


#2

the closure implementation is being finalised, and the documentation is not up to date. in particular, the & character is temporarily necessary - it means a by-reference closure, of an anonymous type which impls the trait Fn.

at some point the type of closure will be inferred and the marker will no longer be necessary. this might even be implemented before 1.0. in the meantime, use |&:expressionless: for a Fn, which takes self by reference |:| for a FnOnce closure, which takes self by value (and therefore takes ownership of it) |&mut:| for a FnMut, which takes &mut self (and can therefore modify captured variables


#3

OK, thanks. Just to show the use case, here’s what I’m using a closure for. This is a typical “find” operation in a DOM-type tree structure. “Element” here is from Florob/RustyXML.

 //
 //  find_all --  find all elements for which test function returns true.
 //
 //  Someday this will accept unboxed lambdas for testfn,
 //  but that's not working yet in Rust 1.0 pre-alpha
 //
 pub fn find_all<'a>(tree: &'a xml::Element, testfn: fn(&xml::Element) -> bool, finds: &mut Vec<&'a xml::Element>, recurse: bool) {
     if testfn(tree)    // if match 
     {   finds.push(tree);   // save if match on name
         if recurse == false { return }  // explore finds?
     }
     for child in tree.children.iter() {   
         match *child {    // child is an Xml enum
             xml::Xml::ElementNode(ref childelt) => {
                 find_all(childelt, testfn, finds, recurse ); }, // recurse
             _ => () // ignore non ElementNode children
         }   // end match child
     }
 }

Usage currently looks like

let mut items = Vec::<&xml::Element>::new(); // accumulate ITEM entries
fn isitem(e: &xml::Element) -> bool { e.name == "item" };// someday this will be an unboxed lambda
find_all(tree, isitem, &mut items, false); // find all ITEM elements

Works fine, but requires defining a named function.

As I understand it, once unboxed lambdas are implemented, this can be replaced with the more concise form

let mut items = Vec::<&xml::Element>::new();  
find_all(tree, |e| e.name == "item", &mut items, false);  

That’s clearly a lambda that needs no boxing; it imports nothing from outer scopes. That will be convenient; this idiom comes up in most HTML/XML/JSON tree processing. Is that a correct understanding of what’s coming?


#4

Must a lambda be a closure? A pure function (one whose result is dependent only on its arguments) need not be a closure. Rust supports this, but not when the function is written as a lambda.

The Rust compiler detects the difference between a pure function and a closure. An internal function with an imported variable is recognized as a closure. The error message for that is: “error: can’t capture dynamic environment in a fn item; use the || { … } closure form instead”.

Functions which accept lambda parameters invoke a whole generic mechanism, generating a new instance of the function for each call. That’s unnecessary when a lambda is a pure function. I’d argue for supporting pure-functoin lambdas as functions, acceptable as plain function parameters.