Maybe the tutorial section on functions should completely gloss over fn~, and save it for the section on tasks. It is just a tutorial, it doesn't have to justify every inch of the language.
----- Original Message ----- > From: "Brian Anderson" <[email protected]> > To: "Niko Matsakis" <[email protected]> > Cc: [email protected] > Sent: Thursday, January 12, 2012 3:49:49 PM > Subject: Re: [rust-dev] closure types > > 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" <[email protected]> > > To: [email protected] > > 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 > > [email protected] > > https://mail.mozilla.org/listinfo/rust-dev > > > _______________________________________________ Rust-dev mailing list [email protected] https://mail.mozilla.org/listinfo/rust-dev
