about summary refs log tree commit diff
path: root/doc/tutorial/args.md
blob: d02e9236c576ecadb8c20cf02a8035cb961db3e2 (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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# Argument passing

Rust datatypes are not trivial to copy (the way, for example,
JavaScript values can be copied by simply taking one or two machine
words and plunking them somewhere else). Shared boxes require
reference count updates, big records, tags, or unique pointers require
an arbitrary amount of data to be copied (plus updating the reference
counts of shared boxes hanging off them).

For this reason, the default calling convention for Rust functions
leaves ownership of the arguments with the caller. The caller
guarantees that the arguments will outlive the call, the callee merely
gets access to them.

## Safe references

There is one catch with this approach: sometimes the compiler can
*not* statically guarantee that the argument value at the caller side
will survive to the end of the call. Another argument might indirectly
refer to it and be used to overwrite it, or a closure might assign a
new value to it.

Fortunately, Rust tasks are single-threaded worlds, which share no
data with other tasks, and that most data is immutable. This allows
most argument-passing situations to be proved safe without further
difficulty.

Take the following program:

    # fn get_really_big_record() -> int { 1 }
    # fn myfunc(a: int) {}
    fn main() {
        let x = get_really_big_record();
        myfunc(x);
    }

Here we know for sure that no one else has access to the `x` variable
in `main`, so we're good. But the call could also look like this:

    # fn myfunc(a: int, b: block()) {}
    # fn get_another_record() -> int { 1 }
    # let x = 1;
    myfunc(x, {|| x = get_another_record(); });

Now, if `myfunc` first calls its second argument and then accesses its
first argument, it will see a different value from the one that was
passed to it.

In such a case, the compiler will insert an implicit copy of `x`,
*except* if `x` contains something mutable, in which case a copy would
result in code that behaves differently. If copying `x` might be
expensive (for example, if it holds a vector), the compiler will emit
a warning.

There are even more tricky cases, in which the Rust compiler is forced
to pessimistically assume a value will get mutated, even though it is
not sure.

    fn for_each(v: [mutable @int], iter: block(@int)) {
       for elt in v { iter(elt); }
    }

For all this function knows, calling `iter` (which is a closure that
might have access to the vector that's passed as `v`) could cause the
elements in the vector to be mutated, with the effect that it can not
guarantee that the boxes will live for the duration of the call. So it
has to copy them. In this case, this will happen implicitly (bumping a
reference count is considered cheap enough to not warn about it).

## The copy operator

If the `for_each` function given above were to take a vector of
`{mutable a: int}` instead of `@int`, it would not be able to
implicitly copy, since if the `iter` function changes a copy of a
mutable record, the changes won't be visible in the record itself. If
we *do* want to allow copies there, we have to explicitly allow it
with the `copy` operator:

    type mutrec = {mutable x: int};
    fn for_each(v: [mutable mutrec], iter: block(mutrec)) {
       for elt in v { iter(copy elt); }
    }

Adding a `copy` operator is also the way to muffle warnings about
implicit copies.

## Other uses of safe references

Safe references are not only used for argument passing. When you
destructure on a value in an `alt` expression, or loop over a vector
with `for`, variables bound to the inside of the given data structure
will use safe references, not copies. This means such references are
very cheap, but you'll occasionally have to copy them to ensure
safety.

    let my_rec = {a: 4, b: [1, 2, 3]};
    alt my_rec {
      {a, b} {
        log(info, b); // This is okay
        my_rec = {a: a + 1, b: b + [a]};
        log(info, b); // Here reference b has become invalid
      }
    }

## Argument passing styles

The fact that arguments are conceptually passed by safe reference does
not mean all arguments are passed by pointer. Composite types like
records and tags *are* passed by pointer, but single-word values, like
integers and pointers, are simply passed by value. Most of the time,
the programmer does not have to worry about this, as the compiler will
simply pick the most efficient passing style. There is one exception,
which will be described in the section on [generics](generic.html).

To explicitly set the passing-style for a parameter, you prefix the
argument name with a sigil. There are two special passing styles that
are often useful. The first is by-mutable-pointer, written with a
single `&`:

    fn vec_push(&v: [int], elt: int) {
        v += [elt];
    }

This allows the function to mutate the value of the argument, *in the
caller's context*. Clearly, you are only allowed to pass things that
can actually be mutated to such a function.

Then there is the by-copy style, written `+`. This indicates that the
function wants to take ownership of the argument value. If the caller
does not use the argument after the call, it will be 'given' to the
callee. Otherwise a copy will be made. This mode is mostly used for
functions that construct data structures. The argument will end up
being owned by the data structure, so if that can be done without a
copy, that's a win.

    type person = {name: str, address: str};
    fn make_person(+name: str, +address: str) -> person {
        ret {name: name, address: address};
    }