Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 51 additions & 9 deletions doc/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -863,11 +863,34 @@ allocating memory and indirecting through a pointer. But for big structs, or
those with mutable fields, it can be useful to have a single copy on
the stack or on the heap, and refer to that through a pointer.

Rust supports several types of pointers. The safe pointer types are
`@T`, for managed boxes allocated on the local heap, `~T`, for
uniquely-owned boxes allocated on the exchange heap, and `&T`, for
borrowed pointers, which may point to any memory, and whose lifetimes
are governed by the call stack.
Whenever memory is allocated on the heap, the program needs a strategy to
dispose of the memory when no longer needed. Most languages, such as Java or
Python, use *garbage collection* for this, a strategy in which the program
periodically searches for allocations that are no longer reachable in order
to dispose of them. Other languages, such as C, use *manual memory
management*, which relies on the programmer to specify when memory should be
reclaimed.

Rust is in a different position. It differs from the garbage-collected
environments in that allows the programmer to choose the disposal
strategy on an object-by-object basis. Not only does this have benefits for
performance, but we will later see that this model has benefits for
concurrency as well, by making it possible for the Rust compiler to detect
data races at compile time. Rust also differs from the manually managed
languages in that it is *safe*—it uses a [pointer lifetime
analysis][borrow] to ensure that manual memory management cannot cause memory
errors at runtime.

[borrow]: tutorial-borrowed-ptr.html

The cornerstone of Rust's memory management is the concept of a *smart
pointer*—a pointer type that indicates the lifetime of the object it points
to. This solution is familiar to C++ programmers; Rust differs from C++,
however, in that a small set of smart pointers are built into the language.
The safe pointer types are `@T`, for *managed* boxes allocated on the *local
heap*, `~T`, for *uniquely-owned* boxes allocated on the *exchange
heap*, and `&T`, for *borrowed* pointers, which may point to any memory, and
whose lifetimes are governed by the call stack.

All pointer types can be dereferenced with the `*` unary operator.

Expand Down Expand Up @@ -919,7 +942,17 @@ node2.next = SomeNode(node3);
node3.prev = SomeNode(node2);
~~~

Managed boxes never cross task boundaries.
Managed boxes never cross task boundaries. This has several benefits for
performance:

* The Rust garbage collector does not need to stop multiple threads in order
to collect garbage.

* You can separate your application into "real-time" tasks that do not use
the garbage collector and "non-real-time" tasks that do, and the real-time
tasks will not be interrupted by the non-real-time tasks.

C++ programmers will recognize `@T` as similar to `std::shared_ptr<T>`.

> ***Note:*** Currently, the Rust compiler generates code to reclaim
> managed boxes through reference counting and a cycle collector, but
Expand Down Expand Up @@ -956,10 +989,19 @@ let z = *x + *y;
assert z == 20;
~~~~

Owned boxes, when they do not contain any managed boxes, can be sent
to other tasks. The sending task will give up ownership of the box,
When they do not contain any managed boxes, owned boxes can be sent
to other tasks. The sending task will give up ownership of the box
and won't be able to access it afterwards. The receiving task will
become the sole owner of the box.
become the sole owner of the box. This prevents *data races*—errors
that could otherwise result from multiple tasks working on the same
data without synchronization.

When an owned pointer goes out of scope or is overwritten, the object
it points to is immediately freed. Effective use of owned boxes can
therefore be an efficient alternative to garbage collection.

C++ programmers will recognize `~T` as similar to `std::unique_ptr<T>`
(or `std::auto_ptr<T>` in C++03 and below).

## Borrowed pointers

Expand Down
12 changes: 5 additions & 7 deletions src/librustc/front/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,19 +145,17 @@ fn fold_item(cx: test_ctxt, &&i: @ast::item, fld: fold::ast_fold) ->
}

fn is_test_fn(i: @ast::item) -> bool {
let has_test_attr =
vec::len(attr::find_attrs_by_name(i.attrs, ~"test")) > 0u;
let has_test_attr = attr::find_attrs_by_name(i.attrs,
~"test").is_not_empty();

fn has_test_signature(i: @ast::item) -> bool {
match /*bad*/copy i.node {
ast::item_fn(decl, _, tps, _) => {
let input_cnt = vec::len(decl.inputs);
match &i.node {
&ast::item_fn(ref decl, _, ref tps, _) => {
let no_output = match decl.output.node {
ast::ty_nil => true,
_ => false
};
let tparm_cnt = vec::len(tps);
input_cnt == 0u && no_output && tparm_cnt == 0u
decl.inputs.is_empty() && no_output && tps.is_empty()
}
_ => false
}
Expand Down
8 changes: 4 additions & 4 deletions src/librustc/middle/liveness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1662,7 +1662,7 @@ fn check_fn(_fk: visit::fn_kind, _decl: fn_decl,
enum ReadKind {
PossiblyUninitializedVariable,
PossiblyUninitializedField,
MovedVariable
MovedValue
}

impl @Liveness {
Expand Down Expand Up @@ -1815,7 +1815,7 @@ impl @Liveness {
lnk: LiveNodeKind,
var: Variable) {

// the only time that it is possible to have a moved variable
// the only time that it is possible to have a moved value
// used by ExitNode would be arguments or fields in a ctor.
// we give a slightly different error message in those cases.
if lnk == ExitNode {
Expand All @@ -1837,7 +1837,7 @@ impl @Liveness {
}
}

self.report_illegal_read(move_span, lnk, var, MovedVariable);
self.report_illegal_read(move_span, lnk, var, MovedValue);
self.tcx.sess.span_note(
move_span, ~"move of variable occurred here");

Expand All @@ -1852,7 +1852,7 @@ impl @Liveness {
~"possibly uninitialized variable"
}
PossiblyUninitializedField => ~"possibly uninitialized field",
MovedVariable => ~"moved variable"
MovedValue => ~"moved value"
};
let name = (*self.ir).variable_name(var);
match lnk {
Expand Down
48 changes: 46 additions & 2 deletions src/libstd/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

#[forbid(deprecated_mode)];

use core::cmp::Eq;
use core::cmp::{Eq, Ord};
use core::int;
use core::libc::{c_char, c_int, c_long, size_t, time_t};
use core::i32;
Expand Down Expand Up @@ -41,7 +41,7 @@ extern mod rustrt {
pub struct Timespec { sec: i64, nsec: i32 }

impl Timespec {
static fn new(sec: i64, nsec: i32) -> Timespec {
static pure fn new(sec: i64, nsec: i32) -> Timespec {
Timespec { sec: sec, nsec: nsec }
}
}
Expand All @@ -53,6 +53,16 @@ impl Timespec : Eq {
pure fn ne(&self, other: &Timespec) -> bool { !self.eq(other) }
}

impl Timespec : Ord {
pure fn lt(&self, other: &Timespec) -> bool {
self.sec < other.sec ||
(self.sec == other.sec && self.nsec < other.nsec)
}
pure fn le(&self, other: &Timespec) -> bool { !other.lt(self) }
pure fn ge(&self, other: &Timespec) -> bool { !self.lt(other) }
pure fn gt(&self, other: &Timespec) -> bool { !self.le(other) }
}

/**
* Returns the current time as a `timespec` containing the seconds and
* nanoseconds since 1970-01-01T00:00:00Z.
Expand Down Expand Up @@ -1247,4 +1257,38 @@ mod tests {
assert utc.rfc822z() == ~"Fri, 13 Feb 2009 23:31:30 -0000";
assert utc.rfc3339() == ~"2009-02-13T23:31:30Z";
}

#[test]
fn test_timespec_eq_ord() {
use core::cmp::{eq, ge, gt, le, lt, ne};

let a = &Timespec::new(-2, 1);
let b = &Timespec::new(-1, 2);
let c = &Timespec::new(1, 2);
let d = &Timespec::new(2, 1);
let e = &Timespec::new(2, 1);

assert eq(d, e);
assert ne(c, e);

assert lt(a, b);
assert lt(b, c);
assert lt(c, d);

assert le(a, b);
assert le(b, c);
assert le(c, d);
assert le(d, e);
assert le(e, d);

assert ge(b, a);
assert ge(c, b);
assert ge(d, c);
assert ge(e, d);
assert ge(d, e);

assert gt(b, a);
assert gt(c, b);
assert gt(d, c);
}
}
2 changes: 1 addition & 1 deletion src/test/compile-fail/alt-vec-tail-move.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ fn main() {
[1, 2, ..move tail] => tail,
_ => core::util::unreachable()
};
a[0] = 0; //~ ERROR: use of moved variable
a[0] = 0; //~ ERROR: use of moved value
}
2 changes: 1 addition & 1 deletion src/test/compile-fail/cap-clause-use-after-move.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@
fn main() {
let x = 5;
let _y = fn~(move x) { }; //~ WARNING captured variable `x` not used in closure
let _z = x; //~ ERROR use of moved variable: `x`
let _z = x; //~ ERROR use of moved value: `x`
}
2 changes: 1 addition & 1 deletion src/test/compile-fail/liveness-move-from-mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ fn main() {

let x: int = 25;
loop {
take(move x); //~ ERROR use of moved variable: `x`
take(move x); //~ ERROR use of moved value: `x`
//~^ NOTE move of variable occurred here
}
}
8 changes: 4 additions & 4 deletions src/test/compile-fail/liveness-move-in-loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ fn main() {
loop {
loop {
// tjc: Not sure why it prints the same error twice
x = move y; //~ ERROR use of moved variable
//~^ NOTE move of variable occurred here
//~^^ ERROR use of moved variable
//~^^^ NOTE move of variable occurred here
x = move y; //~ ERROR use of moved value
//~^ NOTE move of value occurred here
//~^^ ERROR use of moved value
//~^^^ NOTE move of value occurred here

copy x;
}
Expand Down
8 changes: 4 additions & 4 deletions src/test/compile-fail/liveness-move-in-while.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ fn main() {
log(debug, y);
// tjc: not sure why it prints the same error twice
while true { while true { while true { x = move y; copy x; } } }
//~^ ERROR use of moved variable: `y`
//~^^ NOTE move of variable occurred here
//~^^^ ERROR use of moved variable: `y`
//~^^^^ NOTE move of variable occurred here
//~^ ERROR use of moved value: `y`
//~^^ NOTE move of value occurred here
//~^^^ ERROR use of moved value: `y`
//~^^^^ NOTE move of value occurred here
}
}
4 changes: 2 additions & 2 deletions src/test/compile-fail/liveness-use-after-move.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

fn main() {
let x = @5;
let y = move x; //~ NOTE move of variable occurred here
log(debug, *x); //~ ERROR use of moved variable: `x`
let y = move x; //~ NOTE move of value occurred here
log(debug, *x); //~ ERROR use of moved value: `x`
copy y;
}
4 changes: 2 additions & 2 deletions src/test/compile-fail/liveness-use-after-send.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ enum _chan<T> = int;
// Tests that "log(debug, message);" is flagged as using
// message after the send deinitializes it
fn test00_start(ch: _chan<int>, message: int, _count: int) {
send(ch, move message); //~ NOTE move of variable occurred here
log(debug, message); //~ ERROR use of moved variable: `message`
send(ch, move message); //~ NOTE move of value occurred here
log(debug, message); //~ ERROR use of moved value: `message`
}

fn main() { fail; }
2 changes: 1 addition & 1 deletion src/test/compile-fail/move-based-on-type-tuple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

fn dup(x: ~int) -> ~(~int,~int) { ~(x, x) } //~ ERROR use of moved variable
fn dup(x: ~int) -> ~(~int,~int) { ~(x, x) } //~ ERROR use of moved value
fn main() {
dup(~3);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ fn main() {
do task::spawn {
io::println(x);
}
io::println(x); //~ ERROR use of moved variable
io::println(x); //~ ERROR use of moved value
}

2 changes: 1 addition & 1 deletion src/test/compile-fail/no-capture-arc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// error-pattern: use of moved variable
// error-pattern: use of moved value

extern mod std;
use std::arc;
Expand Down
4 changes: 2 additions & 2 deletions src/test/compile-fail/no-reuse-move-arc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ fn main() {
let v = ~[1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let arc_v = arc::ARC(v);

do task::spawn() |move arc_v| { //~ NOTE move of variable occurred here
do task::spawn() |move arc_v| { //~ NOTE move of value occurred here
let v = *arc::get(&arc_v);
assert v[3] == 4;
};

assert (*arc::get(&arc_v))[2] == 3; //~ ERROR use of moved variable: `arc_v`
assert (*arc::get(&arc_v))[2] == 3; //~ ERROR use of moved value: `arc_v`

log(info, arc_v);
}
2 changes: 1 addition & 1 deletion src/test/compile-fail/unary-move.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// error-pattern: use of moved variable
// error-pattern: use of moved value

fn main() {
let x = 3;
Expand Down
2 changes: 1 addition & 1 deletion src/test/compile-fail/use-after-move-based-on-type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@
fn main() {
let x = ~"Hello!";
let _y = x;
io::println(x); //~ ERROR use of moved variable
io::println(x); //~ ERROR use of moved value
}

2 changes: 1 addition & 1 deletion src/test/compile-fail/use-after-move-self-based-on-type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ struct S {
impl S {
fn foo(self) -> int {
self.bar();
return self.x; //~ ERROR use of moved variable
return self.x; //~ ERROR use of moved value
}

fn bar(self) {}
Expand Down
2 changes: 1 addition & 1 deletion src/test/compile-fail/use-after-move-self.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ struct S {
impl S {
fn foo(self) -> int {
(move self).bar();
return self.x; //~ ERROR use of moved variable
return self.x; //~ ERROR use of moved value
}

fn bar(self) {}
Expand Down