diff --git a/doc/tutorial.md b/doc/tutorial.md index 40dba7afda4d6..e26651508efc5 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -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. @@ -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`. > ***Note:*** Currently, the Rust compiler generates code to reclaim > managed boxes through reference counting and a cycle collector, but @@ -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` +(or `std::auto_ptr` in C++03 and below). ## Borrowed pointers diff --git a/src/librustc/front/test.rs b/src/librustc/front/test.rs index 17de40b964256..0f80d00369021 100644 --- a/src/librustc/front/test.rs +++ b/src/librustc/front/test.rs @@ -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 } diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index bfcd809594b2b..fead1f3352ec8 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -1662,7 +1662,7 @@ fn check_fn(_fk: visit::fn_kind, _decl: fn_decl, enum ReadKind { PossiblyUninitializedVariable, PossiblyUninitializedField, - MovedVariable + MovedValue } impl @Liveness { @@ -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 { @@ -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"); @@ -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 { diff --git a/src/libstd/time.rs b/src/libstd/time.rs index 8cc6d0245a1e3..ddd5662122756 100644 --- a/src/libstd/time.rs +++ b/src/libstd/time.rs @@ -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; @@ -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 } } } @@ -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. @@ -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); + } } diff --git a/src/test/compile-fail/alt-vec-tail-move.rs b/src/test/compile-fail/alt-vec-tail-move.rs index fc7d663713195..cd85cb197781f 100644 --- a/src/test/compile-fail/alt-vec-tail-move.rs +++ b/src/test/compile-fail/alt-vec-tail-move.rs @@ -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 } diff --git a/src/test/compile-fail/cap-clause-use-after-move.rs b/src/test/compile-fail/cap-clause-use-after-move.rs index 1091a5a9faf43..097158843a295 100644 --- a/src/test/compile-fail/cap-clause-use-after-move.rs +++ b/src/test/compile-fail/cap-clause-use-after-move.rs @@ -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` } diff --git a/src/test/compile-fail/liveness-move-from-mode.rs b/src/test/compile-fail/liveness-move-from-mode.rs index ccf3e7abcdb36..383bd02c0c240 100644 --- a/src/test/compile-fail/liveness-move-from-mode.rs +++ b/src/test/compile-fail/liveness-move-from-mode.rs @@ -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 } } diff --git a/src/test/compile-fail/liveness-move-in-loop.rs b/src/test/compile-fail/liveness-move-in-loop.rs index 499edcd384ff6..0935d42996654 100644 --- a/src/test/compile-fail/liveness-move-in-loop.rs +++ b/src/test/compile-fail/liveness-move-in-loop.rs @@ -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; } diff --git a/src/test/compile-fail/liveness-move-in-while.rs b/src/test/compile-fail/liveness-move-in-while.rs index 5f3219c5914e3..9f261fdb2d1ba 100644 --- a/src/test/compile-fail/liveness-move-in-while.rs +++ b/src/test/compile-fail/liveness-move-in-while.rs @@ -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 } } diff --git a/src/test/compile-fail/liveness-use-after-move.rs b/src/test/compile-fail/liveness-use-after-move.rs index f7ee3c7992ddd..d48cbc23e158e 100644 --- a/src/test/compile-fail/liveness-use-after-move.rs +++ b/src/test/compile-fail/liveness-use-after-move.rs @@ -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; } diff --git a/src/test/compile-fail/liveness-use-after-send.rs b/src/test/compile-fail/liveness-use-after-send.rs index 074bdf42280f4..2a1e486d13356 100644 --- a/src/test/compile-fail/liveness-use-after-send.rs +++ b/src/test/compile-fail/liveness-use-after-send.rs @@ -19,8 +19,8 @@ enum _chan = int; // Tests that "log(debug, message);" is flagged as using // message after the send deinitializes it fn test00_start(ch: _chan, 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; } diff --git a/src/test/compile-fail/move-based-on-type-tuple.rs b/src/test/compile-fail/move-based-on-type-tuple.rs index 9208254080772..6d5bb638be69b 100644 --- a/src/test/compile-fail/move-based-on-type-tuple.rs +++ b/src/test/compile-fail/move-based-on-type-tuple.rs @@ -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); } diff --git a/src/test/compile-fail/moves-based-on-type-capture-clause-bad.rs b/src/test/compile-fail/moves-based-on-type-capture-clause-bad.rs index 26c1e1360e9e1..57829e72674e6 100644 --- a/src/test/compile-fail/moves-based-on-type-capture-clause-bad.rs +++ b/src/test/compile-fail/moves-based-on-type-capture-clause-bad.rs @@ -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 } diff --git a/src/test/compile-fail/no-capture-arc.rs b/src/test/compile-fail/no-capture-arc.rs index 7dc3c247ea48e..c5a5f9e74d0db 100644 --- a/src/test/compile-fail/no-capture-arc.rs +++ b/src/test/compile-fail/no-capture-arc.rs @@ -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; diff --git a/src/test/compile-fail/no-reuse-move-arc.rs b/src/test/compile-fail/no-reuse-move-arc.rs index 8a7c37649d2ed..a959772f5d246 100644 --- a/src/test/compile-fail/no-reuse-move-arc.rs +++ b/src/test/compile-fail/no-reuse-move-arc.rs @@ -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); } diff --git a/src/test/compile-fail/unary-move.rs b/src/test/compile-fail/unary-move.rs index b04a57fcac1ea..2afecee6e1c55 100644 --- a/src/test/compile-fail/unary-move.rs +++ b/src/test/compile-fail/unary-move.rs @@ -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; diff --git a/src/test/compile-fail/use-after-move-based-on-type.rs b/src/test/compile-fail/use-after-move-based-on-type.rs index ad0248ac07d46..6c268c5e13c83 100644 --- a/src/test/compile-fail/use-after-move-based-on-type.rs +++ b/src/test/compile-fail/use-after-move-based-on-type.rs @@ -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 } diff --git a/src/test/compile-fail/use-after-move-self-based-on-type.rs b/src/test/compile-fail/use-after-move-self-based-on-type.rs index ee09075c11ee9..270fe3626e8a7 100644 --- a/src/test/compile-fail/use-after-move-self-based-on-type.rs +++ b/src/test/compile-fail/use-after-move-self-based-on-type.rs @@ -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) {} diff --git a/src/test/compile-fail/use-after-move-self.rs b/src/test/compile-fail/use-after-move-self.rs index 7bc1979fb8f4f..8ba58cf6f66fe 100644 --- a/src/test/compile-fail/use-after-move-self.rs +++ b/src/test/compile-fail/use-after-move-self.rs @@ -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) {}