diff --git a/configure b/configure index 6cdbfadf63798..f379545f99165 100755 --- a/configure +++ b/configure @@ -904,6 +904,7 @@ do make_dir $h/test/doc-guide-runtime make_dir $h/test/doc-guide-macros make_dir $h/test/doc-guide-lifetimes + make_dir $h/test/doc-guide-ownership make_dir $h/test/doc-guide-pointers make_dir $h/test/doc-guide-container make_dir $h/test/doc-guide-tasks diff --git a/mk/docs.mk b/mk/docs.mk index 34031aded5a4a..11ff7ca9304db 100644 --- a/mk/docs.mk +++ b/mk/docs.mk @@ -27,7 +27,7 @@ ###################################################################### DOCS := index intro tutorial guide guide-ffi guide-macros guide-lifetimes \ guide-tasks guide-container guide-pointers guide-testing \ - guide-runtime complement-bugreport \ + guide-runtime guide-ownership complement-bugreport \ complement-lang-faq complement-design-faq complement-project-faq rust \ rustdoc guide-unsafe guide-strings diff --git a/src/doc/guide-lifetimes.md b/src/doc/guide-lifetimes.md index 66faee3fa04c8..f3daf59ca7535 100644 --- a/src/doc/guide-lifetimes.md +++ b/src/doc/guide-lifetimes.md @@ -1,568 +1,3 @@ % The Rust References and Lifetimes Guide -# Introduction - -References are one of the more flexible and powerful tools available in -Rust. They can point anywhere: into the heap, stack, and even into the -interior of another data structure. A reference is as flexible as a C pointer -or C++ reference. - -Unlike C and C++ compilers, the Rust compiler includes special static -checks that ensure that programs use references safely. - -Despite their complete safety, a reference's representation at runtime -is the same as that of an ordinary pointer in a C program. They introduce zero -overhead. The compiler does all safety checks at compile time. - -Although references have rather elaborate theoretical underpinnings -(e.g. region pointers), the core concepts will be familiar to anyone -who has worked with C or C++. The best way to explain how they are -used—and their limitations—is probably just to work through several examples. - -# By example - -References, sometimes known as *borrowed pointers*, are only valid for -a limited duration. References never claim any kind of ownership -over the data that they point to. Instead, they are used for cases -where you would like to use data for a short time. - -Consider a simple struct type `Point`: - -~~~ -struct Point {x: f64, y: f64} -~~~ - -We can use this simple definition to allocate points in many different ways. For -example, in this code, each of these local variables contains a point, -but allocated in a different place: - -~~~ -# struct Point {x: f64, y: f64} -let on_the_stack : Point = Point {x: 3.0, y: 4.0}; -let on_the_heap : Box = box Point {x: 7.0, y: 9.0}; -~~~ - -Suppose we wanted to write a procedure that computed the distance between any -two points, no matter where they were stored. One option is to define a function -that takes two arguments of type `Point`—that is, it takes the points by value. -But if we define it this way, calling the function will cause the points to be -copied. For points, this is probably not so bad, but often copies are -expensive. So we'd like to define a function that takes the points just as -a reference. - -~~~ -# struct Point {x: f64, y: f64} -# fn sqrt(f: f64) -> f64 { 0.0 } -fn compute_distance(p1: &Point, p2: &Point) -> f64 { - let x_d = p1.x - p2.x; - let y_d = p1.y - p2.y; - sqrt(x_d * x_d + y_d * y_d) -} -~~~ - -Now we can call `compute_distance()`: - -~~~ -# struct Point {x: f64, y: f64} -# let on_the_stack : Point = Point{x: 3.0, y: 4.0}; -# let on_the_heap : Box = box Point{x: 7.0, y: 9.0}; -# fn compute_distance(p1: &Point, p2: &Point) -> f64 { 0.0 } -compute_distance(&on_the_stack, &*on_the_heap); -~~~ - -Here, the `&` operator takes the address of the variable -`on_the_stack`; this is because `on_the_stack` has the type `Point` -(that is, a struct value) and we have to take its address to get a -value. We also call this _borrowing_ the local variable -`on_the_stack`, because we have created an alias: that is, another -name for the same data. - -Likewise, in the case of `on_the_heap`, -the `&` operator is used in conjunction with the `*` operator -to take a reference to the contents of the box. - -Whenever a caller lends data to a callee, there are some limitations on what -the caller can do with the original. For example, if the contents of a -variable have been lent out, you cannot send that variable to another task. In -addition, the compiler will reject any code that might cause the borrowed -value to be freed or overwrite its component fields with values of different -types (I'll get into what kinds of actions those are shortly). This rule -should make intuitive sense: you must wait for a borrower to return the value -that you lent it (that is, wait for the reference to go out of scope) -before you can make full use of it again. - -# Other uses for the & operator - -In the previous example, the value `on_the_stack` was defined like so: - -~~~ -# struct Point {x: f64, y: f64} -let on_the_stack: Point = Point {x: 3.0, y: 4.0}; -~~~ - -This declaration means that code can only pass `Point` by value to other -functions. As a consequence, we had to explicitly take the address of -`on_the_stack` to get a reference. Sometimes however it is more -convenient to move the & operator into the definition of `on_the_stack`: - -~~~ -# struct Point {x: f64, y: f64} -let on_the_stack2: &Point = &Point {x: 3.0, y: 4.0}; -~~~ - -Applying `&` to an rvalue (non-assignable location) is just a convenient -shorthand for creating a temporary and taking its address. A more verbose -way to write the same code is: - -~~~ -# struct Point {x: f64, y: f64} -let tmp = Point {x: 3.0, y: 4.0}; -let on_the_stack2 : &Point = &tmp; -~~~ - -# Taking the address of fields - -The `&` operator is not limited to taking the address of -local variables. It can also take the address of fields or -individual array elements. For example, consider this type definition -for `Rectangle`: - -~~~ -struct Point {x: f64, y: f64} // as before -struct Size {w: f64, h: f64} // as before -struct Rectangle {origin: Point, size: Size} -~~~ - -Now, as before, we can define rectangles in a few different ways: - -~~~ -# struct Point {x: f64, y: f64} -# struct Size {w: f64, h: f64} // as before -# struct Rectangle {origin: Point, size: Size} -let rect_stack = &Rectangle {origin: Point {x: 1.0, y: 2.0}, - size: Size {w: 3.0, h: 4.0}}; -let rect_heap = box Rectangle {origin: Point {x: 5.0, y: 6.0}, - size: Size {w: 3.0, h: 4.0}}; -~~~ - -In each case, we can extract out individual subcomponents with the `&` -operator. For example, I could write: - -~~~ -# struct Point {x: f64, y: f64} // as before -# struct Size {w: f64, h: f64} // as before -# struct Rectangle {origin: Point, size: Size} -# let rect_stack = &Rectangle {origin: Point {x: 1.0, y: 2.0}, size: Size {w: 3.0, h: 4.0}}; -# let rect_heap = box Rectangle {origin: Point {x: 5.0, y: 6.0}, size: Size {w: 3.0, h: 4.0}}; -# fn compute_distance(p1: &Point, p2: &Point) -> f64 { 0.0 } -compute_distance(&rect_stack.origin, &rect_heap.origin); -~~~ - -which would borrow the field `origin` from the rectangle on the stack -as well as from the owned box, and then compute the distance between them. - -# Lifetimes - -We’ve seen a few examples of borrowing data. To this point, we’ve glossed -over issues of safety. As stated in the introduction, at runtime a reference -is simply a pointer, nothing more. Therefore, avoiding C's problems with -dangling pointers requires a compile-time safety check. - -The basis for the check is the notion of _lifetimes_. A lifetime is a -static approximation of the span of execution during which the pointer -is valid: it always corresponds to some expression or block within the -program. - -The compiler will only allow a borrow *if it can guarantee that the data will -not be reassigned or moved for the lifetime of the pointer*. This does not -necessarily mean that the data is stored in immutable memory. For example, -the following function is legal: - -~~~ -# fn some_condition() -> bool { true } -# struct Foo { f: int } -fn example3() -> int { - let mut x = box Foo {f: 3}; - if some_condition() { - let y = &x.f; // -+ L - return *y; // | - } // -+ - x = box Foo {f: 4}; - // ... -# return 0; -} -~~~ - -Here, the interior of the variable `x` is being borrowed -and `x` is declared as mutable. However, the compiler can prove that -`x` is not assigned anywhere in the lifetime L of the variable -`y`. Therefore, it accepts the function, even though `x` is mutable -and in fact is mutated later in the function. - -It may not be clear why we are so concerned about mutating a borrowed -variable. The reason is that the runtime system frees any box -_as soon as its owning reference changes or goes out of -scope_. Therefore, a program like this is illegal (and would be -rejected by the compiler): - -~~~ {.ignore} -fn example3() -> int { - let mut x = box X {f: 3}; - let y = &x.f; - x = box X {f: 4}; // Error reported here. - *y -} -~~~ - -To make this clearer, consider this diagram showing the state of -memory immediately before the re-assignment of `x`: - -~~~ {.text} - Stack Exchange Heap - - x +-------------+ - | box {f:int} | ----+ - y +-------------+ | - | &int | ----+ - +-------------+ | +---------+ - +--> | f: 3 | - +---------+ -~~~ - -Once the reassignment occurs, the memory will look like this: - -~~~ {.text} - Stack Exchange Heap - - x +-------------+ +---------+ - | box {f:int} | -------> | f: 4 | - y +-------------+ +---------+ - | &int | ----+ - +-------------+ | +---------+ - +--> | (freed) | - +---------+ -~~~ - -Here you can see that the variable `y` still points at the old `f` -property of Foo, which has been freed. - -In fact, the compiler can apply the same kind of reasoning to any -memory that is (uniquely) owned by the stack frame. So we could -modify the previous example to introduce additional owned pointers -and structs, and the compiler will still be able to detect possible -mutations. This time, we'll use an analogy to illustrate the concept. - -~~~ {.ignore} -fn example3() -> int { - struct House { owner: Box } - struct Person { age: int } - - let mut house = box House { - owner: box Person {age: 30} - }; - - let owner_age = &house.owner.age; - house = box House {owner: box Person {age: 40}}; // Error reported here. - house.owner = box Person {age: 50}; // Error reported here. - *owner_age -} -~~~ - -In this case, two errors are reported, one when the variable `house` is -modified and another when `house.owner` is modified. Either modification would -invalidate the pointer `owner_age`. - -# Borrowing and enums - -The previous example showed that the type system forbids any mutations -of owned boxed values while they are being borrowed. In general, the type -system also forbids borrowing a value as mutable if it is already being -borrowed - either as a mutable reference or an immutable one. This restriction -prevents pointers from pointing into freed memory. There is one other -case where the compiler must be very careful to ensure that pointers -remain valid: pointers into the interior of an `enum`. - -Let’s look at the following `shape` type that can represent both rectangles -and circles: - -~~~ -struct Point {x: f64, y: f64}; // as before -struct Size {w: f64, h: f64}; // as before -enum Shape { - Circle(Point, f64), // origin, radius - Rectangle(Point, Size) // upper-left, dimensions -} -~~~ - -Now we might write a function to compute the area of a shape. This -function takes a reference to a shape, to avoid the need for -copying. - -~~~ -# struct Point {x: f64, y: f64}; // as before -# struct Size {w: f64, h: f64}; // as before -# enum Shape { -# Circle(Point, f64), // origin, radius -# Rectangle(Point, Size) // upper-left, dimensions -# } -# static tau: f64 = 6.28; -fn compute_area(shape: &Shape) -> f64 { - match *shape { - Circle(_, radius) => 0.5 * tau * radius * radius, - Rectangle(_, ref size) => size.w * size.h - } -} -~~~ - -The first case matches against circles. Here, the pattern extracts the -radius from the shape variant and the action uses it to compute the -area of the circle. (Like any up-to-date engineer, we use the [tau -circle constant][tau] and not that dreadfully outdated notion of pi). - -[tau]: http://www.math.utah.edu/~palais/pi.html - -The second match is more interesting. Here we match against a -rectangle and extract its size: but rather than copy the `size` -struct, we use a by-reference binding to create a pointer to it. In -other words, a pattern binding like `ref size` binds the name `size` -to a pointer of type `&size` into the _interior of the enum_. - -To make this more clear, let's look at a diagram of memory layout in -the case where `shape` points at a rectangle: - -~~~ {.text} -Stack Memory - -+-------+ +---------------+ -| shape | ------> | rectangle( | -+-------+ | {x: f64, | -| size | -+ | y: f64}, | -+-------+ +----> | {w: f64, | - | h: f64}) | - +---------------+ -~~~ - -Here you can see that rectangular shapes are composed of five words of -memory. The first is a tag indicating which variant this enum is -(`rectangle`, in this case). The next two words are the `x` and `y` -fields for the point and the remaining two are the `w` and `h` fields -for the size. The binding `size` is then a pointer into the inside of -the shape. - -Perhaps you can see where the danger lies: if the shape were somehow -to be reassigned, perhaps to a circle, then although the memory used -to store that shape value would still be valid, _it would have a -different type_! The following diagram shows what memory would look -like if code overwrote `shape` with a circle: - -~~~ {.text} -Stack Memory - -+-------+ +---------------+ -| shape | ------> | circle( | -+-------+ | {x: f64, | -| size | -+ | y: f64}, | -+-------+ +----> | f64) | - | | - +---------------+ -~~~ - -As you can see, the `size` pointer would be pointing at a `f64` -instead of a struct. This is not good: dereferencing the second field -of a `f64` as if it were a struct with two fields would be a memory -safety violation. - -So, in fact, for every `ref` binding, the compiler will impose the -same rules as the ones we saw for borrowing the interior of an owned -box: it must be able to guarantee that the `enum` will not be -overwritten for the duration of the borrow. In fact, the compiler -would accept the example we gave earlier. The example is safe because -the shape pointer has type `&Shape`, which means "reference to -immutable memory containing a `shape`". If, however, the type of that -pointer were `&mut Shape`, then the ref binding would be ill-typed. -Just as with owned boxes, the compiler will permit `ref` bindings -into data owned by the stack frame even if the data are mutable, -but otherwise it requires that the data reside in immutable memory. - -# Returning references - -So far, all of the examples we have looked at, use references in a -“downward” direction. That is, a method or code block creates a -reference, then uses it within the same scope. It is also -possible to return references as the result of a function, but -as we'll see, doing so requires some explicit annotation. - -We could write a subroutine like this: - -~~~ -struct Point {x: f64, y: f64} -fn get_x<'r>(p: &'r Point) -> &'r f64 { &p.x } -~~~ - -Here, the function `get_x()` returns a pointer into the structure it -was given. The type of the parameter (`&'r Point`) and return type -(`&'r f64`) both use a new syntactic form that we have not seen so -far. Here the identifier `r` names the lifetime of the pointer -explicitly. So in effect, this function declares that it takes a -pointer with lifetime `r` and returns a pointer with that same -lifetime. - -In general, it is only possible to return references if they -are derived from a parameter to the procedure. In that case, the -pointer result will always have the same lifetime as one of the -parameters; named lifetimes indicate which parameter that -is. - -In the previous code samples, function parameter types did not include a -lifetime name. The compiler simply creates a fresh name for the lifetime -automatically: that is, the lifetime name is guaranteed to refer to a distinct -lifetime from the lifetimes of all other parameters. - -Named lifetimes that appear in function signatures are conceptually -the same as the other lifetimes we have seen before, but they are a bit -abstract: they don’t refer to a specific expression within `get_x()`, -but rather to some expression within the *caller of `get_x()`*. The -lifetime `r` is actually a kind of *lifetime parameter*: it is defined -by the caller to `get_x()`, just as the value for the parameter `p` is -defined by that caller. - -In any case, whatever the lifetime of `r` is, the pointer produced by -`&p.x` always has the same lifetime as `p` itself: a pointer to a -field of a struct is valid as long as the struct is valid. Therefore, -the compiler accepts the function `get_x()`. - -In general, if you borrow a struct or box to create a -reference, it will only be valid within the function -and cannot be returned. This is why the typical way to return references -is to take references as input (the only other case in -which it can be legal to return a reference is if it -points at a static constant). - -# Named lifetimes - -Lifetimes can be named and referenced. For example, the special lifetime -`'static`, which does not go out of scope, can be used to create global -variables and communicate between tasks (see the manual for use cases). - -## Parameter Lifetimes - -Named lifetimes allow for grouping of parameters by lifetime. -For example, consider this function: - -~~~ -# struct Point {x: f64, y: f64}; // as before -# struct Size {w: f64, h: f64}; // as before -# enum Shape { -# Circle(Point, f64), // origin, radius -# Rectangle(Point, Size) // upper-left, dimensions -# } -# fn compute_area(shape: &Shape) -> f64 { 0.0 } -fn select<'r, T>(shape: &'r Shape, threshold: f64, - a: &'r T, b: &'r T) -> &'r T { - if compute_area(shape) > threshold {a} else {b} -} -~~~ - -This function takes three references and assigns each the same -lifetime `r`. In practice, this means that, in the caller, the -lifetime `r` will be the *intersection of the lifetime of the three -region parameters*. This may be overly conservative, as in this -example: - -~~~ -# struct Point {x: f64, y: f64}; // as before -# struct Size {w: f64, h: f64}; // as before -# enum Shape { -# Circle(Point, f64), // origin, radius -# Rectangle(Point, Size) // upper-left, dimensions -# } -# fn compute_area(shape: &Shape) -> f64 { 0.0 } -# fn select<'r, T>(shape: &Shape, threshold: f64, -# a: &'r T, b: &'r T) -> &'r T { -# if compute_area(shape) > threshold {a} else {b} -# } - // -+ r -fn select_based_on_unit_circle<'r, T>( // |-+ B - threshold: f64, a: &'r T, b: &'r T) -> &'r T { // | | - // | | - let shape = Circle(Point {x: 0., y: 0.}, 1.); // | | - select(&shape, threshold, a, b) // | | -} // |-+ - // -+ -~~~ - -In this call to `select()`, the lifetime of the first parameter shape -is B, the function body. Both of the second two parameters `a` and `b` -share the same lifetime, `r`, which is a lifetime parameter of -`select_based_on_unit_circle()`. The caller will infer the -intersection of these two lifetimes as the lifetime of the returned -value, and hence the return value of `select()` will be assigned a -lifetime of B. This will in turn lead to a compilation error, because -`select_based_on_unit_circle()` is supposed to return a value with the -lifetime `r`. - -To address this, we can modify the definition of `select()` to -distinguish the lifetime of the first parameter from the lifetime of -the latter two. After all, the first parameter is not being -returned. Here is how the new `select()` might look: - -~~~ -# struct Point {x: f64, y: f64}; // as before -# struct Size {w: f64, h: f64}; // as before -# enum Shape { -# Circle(Point, f64), // origin, radius -# Rectangle(Point, Size) // upper-left, dimensions -# } -# fn compute_area(shape: &Shape) -> f64 { 0.0 } -fn select<'r, 'tmp, T>(shape: &'tmp Shape, threshold: f64, - a: &'r T, b: &'r T) -> &'r T { - if compute_area(shape) > threshold {a} else {b} -} -~~~ - -Here you can see that `shape`'s lifetime is now named `tmp`. The -parameters `a`, `b`, and the return value all have the lifetime `r`. -However, since the lifetime `tmp` is not returned, it would be more -concise to just omit the named lifetime for `shape` altogether: - -~~~ -# struct Point {x: f64, y: f64}; // as before -# struct Size {w: f64, h: f64}; // as before -# enum Shape { -# Circle(Point, f64), // origin, radius -# Rectangle(Point, Size) // upper-left, dimensions -# } -# fn compute_area(shape: &Shape) -> f64 { 0.0 } -fn select<'r, T>(shape: &Shape, threshold: f64, - a: &'r T, b: &'r T) -> &'r T { - if compute_area(shape) > threshold {a} else {b} -} -~~~ - -This is equivalent to the previous definition. - -## Labeled Control Structures - -Named lifetime notation can also be used to control the flow of execution: - -~~~ -'h: for i in range(0u, 10) { - 'g: loop { - if i % 2 == 0 { continue 'h; } - if i == 9 { break 'h; } - break 'g; - } -} -~~~ - -> *Note:* Labelled breaks are not currently supported within `while` loops. - -Named labels are hygienic and can be used safely within macros. -See the macros guide section on hygiene for more details. - -# Conclusion - -So there you have it: a (relatively) brief tour of the lifetime -system. For more details, we refer to the (yet to be written) reference -document on references, which will explain the full notation -and give more examples. +This Guide has been deprecated. It has been replaced with the [ownership guide](guide-ownership.html). diff --git a/src/doc/guide-ownership.md b/src/doc/guide-ownership.md new file mode 100644 index 0000000000000..91cb87f1ad7a4 --- /dev/null +++ b/src/doc/guide-ownership.md @@ -0,0 +1,207 @@ +% The Rust Ownership Guide + +One of Rust's most important features is its system of ownership. Other +important concepts like borrowing and lifetimes follow from the ownership +system too. A solid understanding of ownership is what separates an +intermediate Rust programmer from a new one. + +At the same time, ownership is one of the more unique features that Rust has, +and so it may seem a bit strange at first. With a little bit of practice, the +ownership system will become second nature. + +# Ownership + +Virtually all programs make use of some kind of shared resource on your +computer. The most common of these is memory. In order to use a particular +resource, you are given a reference to that resource. It's through this +reference that you are able to utilize the underlying resource. As an example, +here is some Rust code which allocates some memory on the heap, and stores +an integer there: + +```{rust} +fn main() { + let x = box 5i; +} +``` + +The `box` keyword allocates memory on the heap, and places a `Box` there. +`x` is then used to refer to that heap memory. + +Each resource can have at most one **owner**. In this case, `x` is the owner of +the box. An 'owner' is responsible for deallocating, de-provisioning, or +otherwise freeing the particular resource in question. In the given example, +since `x` owns the box, when `x` goes out of scope, the Rust compiler sees that +the owner is going away, and therefore, frees the heap memory for us. This is +the core of Rust's strategy for memory management. Instead of having the +programmer manage deallocating resources, or having a garbage collector pick up +after a programmer, Rust's compiler is able to analyze the ownership of your +resource usage, and free the resource for you. + +# Borrowing + +Of course, it would be very limiting to only have a single possible pointer to +a resource. If you need additional pointers, Rust offers a second concept: +borrowing. Another reference may borrow the contents of a pointer which has +ownership. Here's an example: + +```{rust} +fn add_one(i: &int) -> int { + *i + 1 +} + +fn main() { + let x = 5; + println!("{}", add_one(&x)); +} +``` + +The `&x` on the seventh line creates a reference to `x`, and the `add_one` +function takes a reference to an `int` as a parameter. In this case, the `i` +parameter is said to be **borrowing** the value. + +What's the difference between being an owner and borrowing a reference? A major +difference is that when a reference goes out of scope, the resource is _not_ +deallocated. The owner still exists, of course! If we did a deallocation when a +reference went out of scope, the owner would point to something invalid. There +are some restrictions placed upon when you may borrow a reference, but we'll +discuss those later in this guide. + +# The borrow checker + +It's important to understand that ownership and borrowing are both entirely +_compile time_ concepts in Rust. That is, the rules involving both are checked +entirely at compile time, so you pay no runtime penalty. The part of the +compiler that does this is called 'the borrow checker,' and it's a friend and +foe to all Rustaceans. + +Your first interaction with the borrow checker probably resulted from an +error message that looks a bit like this: + +```{notrust} +error: cannot borrow immutable local variable `x` as mutable + let y = &mut x; + ^ +``` + +or maybe + +```{notrust} + 2:19 error: missing lifetime specifier [E0106] + a_string: &str, + ^~~~ +``` + +or even + +```{notrust} +34:29 error: cannot borrow `env` as mutable more than once at a time + let e = &mut env; + ^~~ +29:29 note: previous borrow of `env` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `env` until the borrow ends + let e = &mut env; + ^~~ +6:6 note: previous borrow ends here + +} +^ +``` + +They indicate that the compiler believes that you're breaking the rules around +who and what can borrow, in what way, and when. But before we get into the +details around those rules, these error messages mention the last concept we +haven't discussed yet: lifetimes. + +# Lifetimes + +A **lifetime** is defined as 'the length of time that a reference is valid.' Why +do we care about how long our references live? Let's see some code: + +```{rust,ignore} +struct SomeStruct { + a_string: &str, +} +``` + +If we try to compile this code, we get an error: + +```{ignore,notrust} +2:19 error: missing lifetime specifier [E0106] +a_string: &str, + ^~~~ +``` + +The error here is 'missing lifetime specifier'. Why do we need a specifier? +Well, consider this code: + +```{rust,ignore} +struct SomeStruct { + a_string: &str, +} + +fn main() { + let x = SomeStruct { a_string: "A string" }; +} +``` + +Here, `x` is a a `SomeStruct`. So we have this pointer to the +structure itself, but the structure _also_ contains a pointer to a `str`. We +have no guarantee that the reference inside of `a_string` is valid for the +entire time that the reference to the `SomeStruct` itself. If it's not, then we +could have a valid reference to a `SomeStruct` which contained an invalid +reference in its `a_string`. That would lead to problems. + +We need a way to tell the compiler that `a_string` must be valid as long as +the struct itself is valid. We can do this with an **explicit +lifetime annotation**: + +```{rust} +struct SomeStruct<'a> { + a_string: &'a str, +} + +fn main() { + let x = box SomeStruct { a_string: "A string" }; +} +``` + +This code compiles. We have two additions: a `<'a>` and a `&'a`. These two are +related. Changing `struct SomeStruct` to `struct SomeStruct<'a>` adds a +**lifetime parameter**. This parameter gives a name to the lifetime of a +reference to this struct. + +Now that we've named the lifetime of a reference to the struct, we can use it +inside the definition. We change `&str` to `&'a str`, which says that +rather than having its own lifetime, `a_string` will have the lifetime of +`'a`, which is what we named the lifetime of the struct itself. Now, +we've tied the two together: we've told Rust that the lifetime of `a_string` +will last as long as the reference to the struct itself. Since they live for +the same amount of time, we'll never have an internal invalid reference, and so +Rust allows the code to compile. + +# The rules of borrowing + +# The rules of lifetime elision + +# Shared ownership + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/doc/guide-pointers.md b/src/doc/guide-pointers.md index 6492400a2cfc7..2ba44e680c35f 100644 --- a/src/doc/guide-pointers.md +++ b/src/doc/guide-pointers.md @@ -408,9 +408,9 @@ test.rs:4 let y = &x; ``` As you might guess, this kind of analysis is complex for a human, and therefore -hard for a computer, too! There is an entire [guide devoted to references -and lifetimes](guide-lifetimes.html) that goes into lifetimes in -great detail, so if you want the full details, check that out. +hard for a computer, too! There is an entire [guide devoted to +ownership](guide-ownership.html) that goes into lifetimes in great detail, so +if you want the full details, check that out. ## Best practices @@ -525,7 +525,7 @@ with some improvements: 4. Rust enforces that no other writeable pointers alias to this heap memory, which means writing to an invalid pointer is not possible. -See the section on references or the [lifetimes guide](guide-lifetimes.html) +See the section on references or the [ownership guide](guide-ownership.html) for more detail on how lifetimes work. Using boxes and references together is very common. For example: @@ -771,5 +771,5 @@ Here's a quick rundown of Rust's pointer types: # Related resources * [API documentation for Box](std/boxed/index.html) -* [Lifetimes guide](guide-lifetimes.html) +* [Ownership guide](guide-ownership.html) * [Cyclone paper on regions](http://www.cs.umd.edu/projects/cyclone/papers/cyclone-regions.pdf), which inspired Rust's lifetime system diff --git a/src/doc/guide-unsafe.md b/src/doc/guide-unsafe.md index a95401682cf0d..017e246cfbc33 100644 --- a/src/doc/guide-unsafe.md +++ b/src/doc/guide-unsafe.md @@ -37,7 +37,7 @@ build safe interfaces. ## References One of Rust's biggest features is memory safety. This is achieved in -part via [the lifetime system](guide-lifetimes.html), which is how the +part via [the lifetime system](guide-ownership.html), which is how the compiler can guarantee that every `&` reference is always valid, and, for example, never pointing to freed memory. diff --git a/src/doc/index.md b/src/doc/index.md index 66f69d62e788f..28f86ce244f3c 100644 --- a/src/doc/index.md +++ b/src/doc/index.md @@ -56,7 +56,7 @@ a guide that can help you out: * [Strings](guide-strings.html) * [Pointers](guide-pointers.html) -* [References and Lifetimes](guide-lifetimes.html) +* [Ownership](guide-ownership.html) * [Containers and Iterators](guide-container.html) * [Tasks and Communication](guide-tasks.html) * [Foreign Function Interface](guide-ffi.html) diff --git a/src/doc/po4a.conf b/src/doc/po4a.conf index d5e386325cbca..202bfccf71850 100644 --- a/src/doc/po4a.conf +++ b/src/doc/po4a.conf @@ -12,6 +12,7 @@ [type: text] src/doc/guide-container.md $lang:doc/l10n/$lang/guide-container.md [type: text] src/doc/guide-ffi.md $lang:doc/l10n/$lang/guide-ffi.md [type: text] src/doc/guide-lifetimes.md $lang:doc/l10n/$lang/guide-lifetimes.md +[type: text] src/doc/guide-ownership.md $lang:doc/l10n/$lang/guide-ownership.md [type: text] src/doc/guide-macros.md $lang:doc/l10n/$lang/guide-macros.md [type: text] src/doc/guide-pointers.md $lang:doc/l10n/$lang/guide-pointers.md [type: text] src/doc/guide-runtime.md $lang:doc/l10n/$lang/guide-runtime.md diff --git a/src/doc/tutorial.md b/src/doc/tutorial.md deleted file mode 100644 index f6e64d8996074..0000000000000 --- a/src/doc/tutorial.md +++ /dev/null @@ -1,3 +0,0 @@ -% The Rust Tutorial - -This tutorial has been deprecated in favor of [the Guide](guide.html). Go check that out instead!