mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-20 02:43:45 +00:00
auto merge of #6070 : thestinger/rust/tutorial, r=pcwalton
This commit is contained in:
commit
5de74f3e75
@ -139,6 +139,74 @@ pub fn uncompress(src: &[u8]) -> Option<~[u8]> {
|
||||
For reference, the examples used here are also available as an [library on
|
||||
GitHub](https://github.com/thestinger/rust-snappy).
|
||||
|
||||
# Destructors
|
||||
|
||||
Foreign libraries often hand off ownership of resources to the calling code,
|
||||
which should be wrapped in a destructor to provide safety and guarantee their
|
||||
release.
|
||||
|
||||
A type with the same functionality as owned boxes can be implemented by
|
||||
wrapping `malloc` and `free`:
|
||||
|
||||
~~~~
|
||||
use core::libc::{c_void, size_t, malloc, free};
|
||||
|
||||
#[abi = "rust-intrinsic"]
|
||||
extern "rust-intrinsic" mod rusti {
|
||||
fn init<T>() -> T;
|
||||
}
|
||||
|
||||
// a wrapper around the handle returned by the foreign code
|
||||
pub struct Unique<T> {
|
||||
priv ptr: *mut T
|
||||
}
|
||||
|
||||
pub impl<'self, T: Owned> Unique<T> {
|
||||
fn new(value: T) -> Unique<T> {
|
||||
unsafe {
|
||||
let ptr = malloc(core::sys::size_of::<T>() as size_t) as *mut T;
|
||||
assert!(!ptr::is_null(ptr));
|
||||
*ptr = value;
|
||||
Unique{ptr: ptr}
|
||||
}
|
||||
}
|
||||
|
||||
// the 'self lifetime results in the same semantics as `&*x` with ~T
|
||||
fn borrow(&self) -> &'self T {
|
||||
unsafe { cast::transmute(self.ptr) }
|
||||
}
|
||||
|
||||
// the 'self lifetime results in the same semantics as `&mut *x` with ~T
|
||||
fn borrow_mut(&mut self) -> &'self mut T {
|
||||
unsafe { cast::transmute(self.ptr) }
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe_destructor]
|
||||
impl<T: Owned> Drop for Unique<T> {
|
||||
fn finalize(&self) {
|
||||
unsafe {
|
||||
let mut x = rusti::init(); // dummy value to swap in
|
||||
x <-> *self.ptr; // moving the object out is needed to call the destructor
|
||||
free(self.ptr as *c_void)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A comparison between the built-in ~ and this reimplementation
|
||||
fn main() {
|
||||
{
|
||||
let mut x = ~5;
|
||||
*x = 10;
|
||||
} // `x` is freed here
|
||||
|
||||
{
|
||||
let mut y = Unique::new(5);
|
||||
*y.borrow_mut() = 10;
|
||||
} // `y` is freed here
|
||||
}
|
||||
~~~~
|
||||
|
||||
# Linking
|
||||
|
||||
In addition to the `#[link_args]` attribute for explicitly passing arguments to the linker, an
|
||||
|
134
doc/tutorial.md
134
doc/tutorial.md
@ -868,108 +868,27 @@ fn first((value, _): (int, float)) -> int { value }
|
||||
|
||||
# Destructors
|
||||
|
||||
C-style resource management requires the programmer to match every allocation
|
||||
with a free, which means manually tracking the responsibility for cleaning up
|
||||
(the owner). Correctness is left to the programmer, and it's easy to get wrong.
|
||||
A *destructor* is a function responsible for cleaning up the resources used by
|
||||
an object when it is no longer accessible. Destructors can be defined to handle
|
||||
the release of resources like files, sockets and heap memory.
|
||||
|
||||
The following code demonstrates manual memory management, in order to contrast
|
||||
it with Rust's resource management. Rust enforces safety, so the `unsafe`
|
||||
keyword is used to explicitly wrap the unsafe code. The keyword is a promise to
|
||||
the compiler that unsafety does not leak outside of the unsafe block, and is
|
||||
used to create safe concepts on top of low-level code.
|
||||
Objects are never accessible after their destructor has been called, so there
|
||||
are no dynamic failures from accessing freed resources. When a task fails, the
|
||||
destructors of all objects in the task are called.
|
||||
|
||||
The `~` sigil represents a unique handle for a memory allocation on the heap:
|
||||
|
||||
~~~~
|
||||
use core::libc::{calloc, free, size_t};
|
||||
|
||||
fn main() {
|
||||
unsafe {
|
||||
let a = calloc(1, int::bytes as size_t);
|
||||
|
||||
let d;
|
||||
|
||||
{
|
||||
let b = calloc(1, int::bytes as size_t);
|
||||
|
||||
let c = calloc(1, int::bytes as size_t);
|
||||
d = c; // move ownership to d
|
||||
|
||||
free(b);
|
||||
}
|
||||
|
||||
free(d);
|
||||
free(a);
|
||||
}
|
||||
{
|
||||
// an integer allocated on the heap
|
||||
let y = ~10;
|
||||
}
|
||||
// the destructor frees the heap memory as soon as `y` goes out of scope
|
||||
~~~~
|
||||
|
||||
Rust uses destructors to handle the release of resources like memory
|
||||
allocations, files and sockets. An object will only be destroyed when there is
|
||||
no longer any way to access it, which prevents dynamic failures from an attempt
|
||||
to use a freed resource. When a task fails, the stack unwinds and the
|
||||
destructors of all objects owned by that task are called.
|
||||
|
||||
The unsafe code from above can be contained behind a safe API that prevents
|
||||
memory leaks or use-after-free:
|
||||
|
||||
~~~~
|
||||
use core::libc::{calloc, free, c_void, size_t};
|
||||
|
||||
struct Blob { priv ptr: *c_void }
|
||||
|
||||
impl Blob {
|
||||
fn new() -> Blob {
|
||||
unsafe { Blob{ptr: calloc(1, int::bytes as size_t)} }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Blob {
|
||||
fn finalize(&self) {
|
||||
unsafe { free(self.ptr); }
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a = Blob::new();
|
||||
|
||||
let d;
|
||||
|
||||
{
|
||||
let b = Blob::new();
|
||||
|
||||
let c = Blob::new();
|
||||
d = c; // move ownership to d
|
||||
|
||||
// b is destroyed here
|
||||
}
|
||||
|
||||
// d is destroyed here
|
||||
// a is destroyed here
|
||||
}
|
||||
~~~~
|
||||
|
||||
This pattern is common enough that Rust includes dynamically allocated memory
|
||||
as first-class types (`~` and `@`). Non-memory resources like files are cleaned
|
||||
up with custom destructors.
|
||||
|
||||
~~~~
|
||||
fn main() {
|
||||
let a = ~0;
|
||||
|
||||
let d;
|
||||
|
||||
{
|
||||
let b = ~0;
|
||||
|
||||
let c = ~0;
|
||||
d = c; // move ownership to d
|
||||
|
||||
// b is destroyed here
|
||||
}
|
||||
|
||||
// d is destroyed here
|
||||
// a is destroyed here
|
||||
}
|
||||
~~~~
|
||||
Rust includes syntax for heap memory allocation in the language since it's
|
||||
commonly used, but the same semantics can be implemented by a type with a
|
||||
custom destructor.
|
||||
|
||||
# Ownership
|
||||
|
||||
@ -984,6 +903,22 @@ and destroy the contained object when they go out of scope. A box managed by
|
||||
the garbage collector starts a new ownership tree, and the destructor is called
|
||||
when it is collected.
|
||||
|
||||
~~~~
|
||||
// the struct owns the objects contained in the `x` and `y` fields
|
||||
struct Foo { x: int, y: ~int }
|
||||
|
||||
{
|
||||
// `a` is the owner of the struct, and thus the owner of the struct's fields
|
||||
let a = Foo { x: 5, y: ~10 };
|
||||
}
|
||||
// when `a` goes out of scope, the destructor for the `~int` in the struct's
|
||||
// field is called
|
||||
|
||||
// `b` is mutable, and the mutability is inherited by the objects it owns
|
||||
let mut b = Foo { x: 5, y: ~10 };
|
||||
b.x = 10;
|
||||
~~~~
|
||||
|
||||
If an object doesn't contain garbage-collected boxes, it consists of a single
|
||||
ownership tree and is given the `Owned` trait which allows it to be sent
|
||||
between tasks. Custom destructors can only be implemented directly on types
|
||||
@ -1007,7 +942,7 @@ refer to that through a pointer.
|
||||
## Owned boxes
|
||||
|
||||
An owned box (`~`) is a uniquely owned allocation on the heap. It inherits the
|
||||
mutability and lifetime of the owner as it would if there was no box.
|
||||
mutability and lifetime of the owner as it would if there was no box:
|
||||
|
||||
~~~~
|
||||
let x = 5; // immutable
|
||||
@ -1021,8 +956,8 @@ let mut y = ~5; // mutable
|
||||
|
||||
The purpose of an owned box is to add a layer of indirection in order to create
|
||||
recursive data structures or cheaply pass around an object larger than a
|
||||
pointer. Since an owned box has a unique owner, it can be used to represent any
|
||||
tree data structure.
|
||||
pointer. Since an owned box has a unique owner, it can only be used to
|
||||
represent a tree data structure.
|
||||
|
||||
The following struct won't compile, because the lack of indirection would mean
|
||||
it has an infinite size:
|
||||
@ -1092,7 +1027,6 @@ d = b; // box type is the same, okay
|
||||
c = b; // error
|
||||
~~~~
|
||||
|
||||
|
||||
# Move semantics
|
||||
|
||||
Rust uses a shallow copy for parameter passing, assignment and returning values
|
||||
|
Loading…
Reference in New Issue
Block a user