about summary refs log tree commit diff
path: root/doc/tutorial/func.md
blob: 15811da04b313487742772d7cc5bbe6963b84ca3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# Functions

Functions (like all other static declarations, such as `type`) can be
declared both at the top level and inside other functions (or modules,
which we'll come back to in moment).

The `ret` keyword immediately returns from a function. It is
optionally followed by an expression to return. In functions that
return `()`, the returned expression can be left off. A function can
also return a value by having its top level block produce an
expression (by omitting the final semicolon).

Some functions (such as the C function `exit`) never return normally.
In Rust, these are annotated with return type `!`:

    fn dead_end() -> ! { fail; }

This helps the compiler avoid spurious error messages. For example,
the following code would be a type error if `dead_end` would be
expected to return.

    # fn can_go_left() -> bool { true }
    # fn can_go_right() -> bool { true }
    # tag dir { left; right; }
    # fn dead_end() -> ! { fail; }
    let dir = if can_go_left() { left }
              else if can_go_right() { right }
              else { dead_end(); };

## Closures

Normal Rust functions (declared with `fn`) do not close over their
environment. A `lambda` expression can be used to create a closure.

    fn make_plus_function(x: int) -> lambda(int) -> int {
        lambda(y: int) -> int { x + y }
    }
    let plus_two = make_plus_function(2);
    assert plus_two(3) == 5;

A `lambda` function *copies* its environment (in this case, the
binding for `x`). It can not mutate the closed-over bindings, and will
not see changes made to these variables after the `lambda` was
evaluated. `lambda`s can be put in data structures and passed around
without limitation.

The type of a closure is `lambda(args) -> type`, as opposed to
`fn(args) -> type`. The `fn` type stands for 'bare' functions, with no
closure attached. Keep this in mind when writing higher-order
functions.

A different form of closure is the block. Blocks are written like they
are in Ruby: `{|x| x + y}`, the formal parameters between pipes,
followed by the function body. They are stack-allocated and properly
close over their environment (they see updates to closed over
variables, for example). But blocks can only be used in a limited set
of circumstances. They can be passed to other functions, but not
stored in data structures or returned.

    fn map_int(f: block(int) -> int, vec: [int]) -> [int] {
        let result = [];
        for i in vec { result += [f(i)]; }
        ret result;
    }
    map_int({|x| x + 1 }, [1, 2, 3]);

The type of blocks is spelled `block(args) -> type`. Both closures and
bare functions are automatically convert to `block`s when appropriate.
Most higher-order functions should take their function arguments as
`block`s.

A block with no arguments is written `{|| body(); }`—you can not leave
off the pipes.

## Binding

Partial application is done using the `bind` keyword in Rust.

    let daynum = bind std::vec::position(_, ["mo", "tu", "we", "do",
                                             "fr", "sa", "su"]);

Binding a function produces a closure (`lambda` type) in which some of
the arguments to the bound function have already been provided.
`daynum` will be a function taking a single string argument, and
returning the day of the week that string corresponds to (if any).

## Iteration

Functions taking blocks provide a good way to define non-trivial
iteration constructs. For example, this one iterates over a vector
of integers backwards:

    fn for_rev(v: [int], act: block(int)) {
        let i = std::vec::len(v);
        while (i > 0u) {
            i -= 1u;
            act(v[i]);
        }
    }

To run such an iteration, you could do this:

    # fn for_rev(v: [int], act: block(int)) {}
    for_rev([1, 2, 3], {|n| log n; });

But Rust allows a more pleasant syntax for this situation, with the
loop block moved out of the parenthesis and the final semicolon
omitted:

    # fn for_rev(v: [int], act: block(int)) {}
    for_rev([1, 2, 3]) {|n|
        log n;
    }