[rust-dev] closure types

Brian Anderson banderson at mozilla.com
Thu Jan 12 15:49:49 PST 2012


I like your new proposal but feel like the tutorial goes backwards. It should say 'pass a function around with type 'fn'; build functions with {|x| }; oh, but you might also be interested in all these caveats'.

----- Original Message -----
> From: "Niko Matsakis" <niko at alum.mit.edu>
> To: rust-dev at mozilla.org
> Sent: Thursday, January 12, 2012 2:28:22 PM
> Subject: [rust-dev] closure types
> 
> So, based on our recent discussions, I was planning to have
> `fn(T)->U`
> be the stack closure type (i.e., block) and `fn@` and `fn~` be the
> boxed
> and unique closure types respectively. `native fn` would be a bare
> function. But this design has never seemed quite right to me: for
> example, is there an explicit syntax for declaring a stack closure?
> If
> so, what is it? `fn() { ... }`?
> 
> Now I am thinking that it makes more sense for the type `fn(T)->U` to
> refer to "any kind of closure" (a kind of abstract supertype). Then
> there are three "concrete" closure types `fn@`, `fn~`, and `fn&`,
> each
> of which are subtypes of `fn(T) -> U`. Bare functions are still
> `native
> fn(T)->U`.
> 
> In some way this is more complex (4 types, not 3) but it also feels
> simpler and cleaner to me. Thoughts?
> 
> I have the changes needed to implement either scheme waiting to be
> pushed. I just didn't push them yet because I didn't feel 100% at
> ease
> with the original scheme. Also, below is my best effort to adapt the
> tutorial to this idea, which may or may not be a good explanation.
> 
> 
> Niko
> 
> ----
> 
> 
> ## Closures
> 
> Named functions, like those in the previous section, do not close
> over
> their environment. Rust also includes support for closures, which are
> functions that can access variables in the scope in which they are
> created.
> 
> There are several forms of closures, each with its own role. The most
> common type is called a 'stack closure' (written `fn&`), this is a
> closure which has full access to its environment.
> 
> fn call_block_with_ten(b: fn&(int)) { b(10); }
> 
> let x = 20;
> call_block_with_ten(fn&(arg: int) {
> #info("x=%d, arg=%d", x, arg);
> });
> 
> This defines a function that accepts a stack closure, and then calls
> it with a simple closure that executes a log statement, accessing
> both
> its argument and the variable `x` from its environment.
> 
> Because stack closures are actually allocated in the stack frame of
> the function where they appear, they are very lightweight and
> efficient. However, they can only be used in a restricted way so as
> to ensure that they do not survive the scope in which they were
> created. They are allowed to appear in function argument position and
> in call position, but nowhere else.
> 
> Note: there is a [more compact, Ruby-like syntax](#shorthand) for
> blocks which infers the types of the arguments. See below for
> details.
> 
> ### Boxed closures
> 
> When you need to store a closure in a data structure, a block will
> not
> do, since the compiler will refuse to let you store it. For this
> purpose, Rust provides a type of closure that has an arbitrary
> lifetime, written `fn@` (boxed closure, analogous to the `@` pointer
> type described in the next section).
> 
> A boxed closure does not directly access its environment, but merely
> copies out the values that it closes over into a private data
> structure. This means that it can not assign to these variables, and
> will not 'see' updates to them.
> 
> This code creates a closure that adds a given string to its argument,
> returns it from a function, and then calls it:
> 
> use std;
> 
> fn mk_appender(suffix: str) -> fn@(str) -> str {
> let f = fn@(s: str) -> str { s + suffix };
> ret f;
> }
> 
> fn main() {
> let shout = mk_appender("!");
> std::io::println(shout("hey ho, let's go"));
> }
> 
> ### Closure compatibility
> 
> It often happens that you want to write a function which can accept
> any kind of closure (e.g., either a stack or a boxed closure). In
> these cases, you can use the type `fn` without any sigil. Thus, when
> writing a higher-order function that wants to do nothing with its
> function argument beyond calling it, you should almost always specify
> the type of that argument as `fn`, so that callers have the
> flexibility to pass whatever they want.
> 
> fn call_twice(f: fn()) { f(); f(); }
> call_twice(fn&() { "I am a block"; });
> call_twice(fn@() { "I am a boxed closure"; });
> fn bare_function() { "I am a plain function"; }
> call_twice(bare_function);
> 
> `fn()` types are subject to the same restrictions as block types;
> that
> is, they may only be passed as parameters or called.
> 
> ### Unique closures
> 
> <a name="unique"></a>
> 
> Unique closures, written `fn~` in analogy to the `~` pointer type
> (see
> next section), hold on to things that can safely be sent between
> processes. They copy the values they close over, much like boxed
> closures, but they also 'own' them—meaning no other code can access
> them. Unique closures mostly exist for spawning new
> [tasks](task.html).
> 
> ### Shorthand syntax
> 
> <a name="shorthand"></a>
> 
> Rust provides a compact closure syntax which can be used to represent
> any kind of closure. The syntax is similar to Ruby or Smalltalk,
> where the arguments are placed in pipes (`|`). For example, `{|arg1,
> arg| body}` defines a closure with two arguments. This syntax is
> intended for use in function calls: the kind of of closure (stack,
> boxed, unique) and the types of the arguments will be inferred based
> on what the function expects to receive.
> 
> As a further simplification, if the final parameter to a function is
> a
> closure, the closure need not be placed within parenthesis. You
> could,
> for example, write...
> 
> let doubled = vec::map([1, 2, 3]) {|x| x*2};
> 
> `vec::map` is a function in the core library that applies its last
> argument to every element of a vector, producing a new vector.
> 
> Even when a closure takes no parameters, you must still write the
> bars
> for the parameter list, as in `{|| ...}`.
> _______________________________________________
> Rust-dev mailing list
> Rust-dev at mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev
> 


More information about the Rust-dev mailing list