Skip to content

Commit 5de74f3

Browse files
committed
auto merge of #6070 : thestinger/rust/tutorial, r=pcwalton
2 parents 9ea32a3 + 195911f commit 5de74f3

File tree

2 files changed

+101
-99
lines changed

2 files changed

+101
-99
lines changed

doc/tutorial-ffi.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,74 @@ pub fn uncompress(src: &[u8]) -> Option<~[u8]> {
139139
For reference, the examples used here are also available as an [library on
140140
GitHub](https://github.com/thestinger/rust-snappy).
141141

142+
# Destructors
143+
144+
Foreign libraries often hand off ownership of resources to the calling code,
145+
which should be wrapped in a destructor to provide safety and guarantee their
146+
release.
147+
148+
A type with the same functionality as owned boxes can be implemented by
149+
wrapping `malloc` and `free`:
150+
151+
~~~~
152+
use core::libc::{c_void, size_t, malloc, free};
153+
154+
#[abi = "rust-intrinsic"]
155+
extern "rust-intrinsic" mod rusti {
156+
fn init<T>() -> T;
157+
}
158+
159+
// a wrapper around the handle returned by the foreign code
160+
pub struct Unique<T> {
161+
priv ptr: *mut T
162+
}
163+
164+
pub impl<'self, T: Owned> Unique<T> {
165+
fn new(value: T) -> Unique<T> {
166+
unsafe {
167+
let ptr = malloc(core::sys::size_of::<T>() as size_t) as *mut T;
168+
assert!(!ptr::is_null(ptr));
169+
*ptr = value;
170+
Unique{ptr: ptr}
171+
}
172+
}
173+
174+
// the 'self lifetime results in the same semantics as `&*x` with ~T
175+
fn borrow(&self) -> &'self T {
176+
unsafe { cast::transmute(self.ptr) }
177+
}
178+
179+
// the 'self lifetime results in the same semantics as `&mut *x` with ~T
180+
fn borrow_mut(&mut self) -> &'self mut T {
181+
unsafe { cast::transmute(self.ptr) }
182+
}
183+
}
184+
185+
#[unsafe_destructor]
186+
impl<T: Owned> Drop for Unique<T> {
187+
fn finalize(&self) {
188+
unsafe {
189+
let mut x = rusti::init(); // dummy value to swap in
190+
x <-> *self.ptr; // moving the object out is needed to call the destructor
191+
free(self.ptr as *c_void)
192+
}
193+
}
194+
}
195+
196+
// A comparison between the built-in ~ and this reimplementation
197+
fn main() {
198+
{
199+
let mut x = ~5;
200+
*x = 10;
201+
} // `x` is freed here
202+
203+
{
204+
let mut y = Unique::new(5);
205+
*y.borrow_mut() = 10;
206+
} // `y` is freed here
207+
}
208+
~~~~
209+
142210
# Linking
143211

144212
In addition to the `#[link_args]` attribute for explicitly passing arguments to the linker, an

doc/tutorial.md

Lines changed: 33 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -868,108 +868,27 @@ fn first((value, _): (int, float)) -> int { value }
868868

869869
# Destructors
870870

871-
C-style resource management requires the programmer to match every allocation
872-
with a free, which means manually tracking the responsibility for cleaning up
873-
(the owner). Correctness is left to the programmer, and it's easy to get wrong.
871+
A *destructor* is a function responsible for cleaning up the resources used by
872+
an object when it is no longer accessible. Destructors can be defined to handle
873+
the release of resources like files, sockets and heap memory.
874874

875-
The following code demonstrates manual memory management, in order to contrast
876-
it with Rust's resource management. Rust enforces safety, so the `unsafe`
877-
keyword is used to explicitly wrap the unsafe code. The keyword is a promise to
878-
the compiler that unsafety does not leak outside of the unsafe block, and is
879-
used to create safe concepts on top of low-level code.
875+
Objects are never accessible after their destructor has been called, so there
876+
are no dynamic failures from accessing freed resources. When a task fails, the
877+
destructors of all objects in the task are called.
880878

881-
~~~~
882-
use core::libc::{calloc, free, size_t};
883-
884-
fn main() {
885-
unsafe {
886-
let a = calloc(1, int::bytes as size_t);
887-
888-
let d;
879+
The `~` sigil represents a unique handle for a memory allocation on the heap:
889880

890-
{
891-
let b = calloc(1, int::bytes as size_t);
892-
893-
let c = calloc(1, int::bytes as size_t);
894-
d = c; // move ownership to d
895-
896-
free(b);
897-
}
898-
899-
free(d);
900-
free(a);
901-
}
902-
}
903881
~~~~
904-
905-
Rust uses destructors to handle the release of resources like memory
906-
allocations, files and sockets. An object will only be destroyed when there is
907-
no longer any way to access it, which prevents dynamic failures from an attempt
908-
to use a freed resource. When a task fails, the stack unwinds and the
909-
destructors of all objects owned by that task are called.
910-
911-
The unsafe code from above can be contained behind a safe API that prevents
912-
memory leaks or use-after-free:
913-
914-
~~~~
915-
use core::libc::{calloc, free, c_void, size_t};
916-
917-
struct Blob { priv ptr: *c_void }
918-
919-
impl Blob {
920-
fn new() -> Blob {
921-
unsafe { Blob{ptr: calloc(1, int::bytes as size_t)} }
922-
}
923-
}
924-
925-
impl Drop for Blob {
926-
fn finalize(&self) {
927-
unsafe { free(self.ptr); }
928-
}
929-
}
930-
931-
fn main() {
932-
let a = Blob::new();
933-
934-
let d;
935-
936-
{
937-
let b = Blob::new();
938-
939-
let c = Blob::new();
940-
d = c; // move ownership to d
941-
942-
// b is destroyed here
943-
}
944-
945-
// d is destroyed here
946-
// a is destroyed here
882+
{
883+
// an integer allocated on the heap
884+
let y = ~10;
947885
}
886+
// the destructor frees the heap memory as soon as `y` goes out of scope
948887
~~~~
949888

950-
This pattern is common enough that Rust includes dynamically allocated memory
951-
as first-class types (`~` and `@`). Non-memory resources like files are cleaned
952-
up with custom destructors.
953-
954-
~~~~
955-
fn main() {
956-
let a = ~0;
957-
958-
let d;
959-
960-
{
961-
let b = ~0;
962-
963-
let c = ~0;
964-
d = c; // move ownership to d
965-
966-
// b is destroyed here
967-
}
968-
969-
// d is destroyed here
970-
// a is destroyed here
971-
}
972-
~~~~
889+
Rust includes syntax for heap memory allocation in the language since it's
890+
commonly used, but the same semantics can be implemented by a type with a
891+
custom destructor.
973892

974893
# Ownership
975894

@@ -984,6 +903,22 @@ and destroy the contained object when they go out of scope. A box managed by
984903
the garbage collector starts a new ownership tree, and the destructor is called
985904
when it is collected.
986905

906+
~~~~
907+
// the struct owns the objects contained in the `x` and `y` fields
908+
struct Foo { x: int, y: ~int }
909+
910+
{
911+
// `a` is the owner of the struct, and thus the owner of the struct's fields
912+
let a = Foo { x: 5, y: ~10 };
913+
}
914+
// when `a` goes out of scope, the destructor for the `~int` in the struct's
915+
// field is called
916+
917+
// `b` is mutable, and the mutability is inherited by the objects it owns
918+
let mut b = Foo { x: 5, y: ~10 };
919+
b.x = 10;
920+
~~~~
921+
987922
If an object doesn't contain garbage-collected boxes, it consists of a single
988923
ownership tree and is given the `Owned` trait which allows it to be sent
989924
between tasks. Custom destructors can only be implemented directly on types
@@ -1007,7 +942,7 @@ refer to that through a pointer.
1007942
## Owned boxes
1008943

1009944
An owned box (`~`) is a uniquely owned allocation on the heap. It inherits the
1010-
mutability and lifetime of the owner as it would if there was no box.
945+
mutability and lifetime of the owner as it would if there was no box:
1011946

1012947
~~~~
1013948
let x = 5; // immutable
@@ -1021,8 +956,8 @@ let mut y = ~5; // mutable
1021956

1022957
The purpose of an owned box is to add a layer of indirection in order to create
1023958
recursive data structures or cheaply pass around an object larger than a
1024-
pointer. Since an owned box has a unique owner, it can be used to represent any
1025-
tree data structure.
959+
pointer. Since an owned box has a unique owner, it can only be used to
960+
represent a tree data structure.
1026961

1027962
The following struct won't compile, because the lack of indirection would mean
1028963
it has an infinite size:
@@ -1092,7 +1027,6 @@ d = b; // box type is the same, okay
10921027
c = b; // error
10931028
~~~~
10941029

1095-
10961030
# Move semantics
10971031

10981032
Rust uses a shallow copy for parameter passing, assignment and returning values

0 commit comments

Comments
 (0)