Document a few undocumented modules in libstd

Hopefull this will make our libstd docs appear a little more "full".
This commit is contained in:
Alex Crichton 2013-09-17 19:42:07 -07:00
parent 0efc4822e9
commit 88bc11e646
6 changed files with 336 additions and 51 deletions

View File

@ -8,6 +8,58 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
C-string manipulation and management
This modules provides the basic methods for creating and manipulating
null-terminated strings for use with FFI calls (back to C). Most C APIs require
that the string being passed to them is null-terminated, and by default rust's
string types are *not* null terminated.
The other problem with translating Rust strings to C strings is that Rust
strings can validly contain a null-byte in the middle of the string (0 is a
valid unicode codepoint). This means that not all Rust strings can actually be
translated to C strings.
# Creation of a C string
A C string is managed through the `CString` type defined in this module. It
"owns" the internal buffer of characters and will automatically deallocate the
buffer when the string is dropped. The `ToCStr` trait is implemented for `&str`
and `&[u8]`, but the conversions can fail due to some of the limitations
explained above.
This also means that currently whenever a C string is created, an allocation
must be performed to place the data elsewhere (the lifetime of the C string is
not tied to the lifetime of the original string/data buffer). If C strings are
heavily used in applications, then caching may be advisable to prevent
unnecessary amounts of allocations.
An example of creating and using a C string would be:
~~~{.rust}
use std::libc;
externfn!(fn puts(s: *libc::c_char))
let my_string = "Hello, world!";
// Allocate the C string with an explicit local that owns the string. The
// `c_buffer` pointer will be deallocated when `my_c_string` goes out of scope.
let my_c_string = my_string.to_c_str();
do my_c_string.with_ref |c_buffer| {
unsafe { puts(c_buffer); }
}
// Don't save off the allocation of the C string, the `c_buffer` will be
// deallocated when this block returns!
do my_string.with_c_str |c_buffer| {
unsafe { puts(c_buffer); }
}
~~~
*/
use cast;
use iter::{Iterator, range};
use libc;

View File

@ -8,71 +8,179 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*! Condition handling */
/*!
#[allow(missing_doc)];
Condition handling
Conditions are a utility used to deal with handling error conditions. The syntax
of a condition handler strikes a resemblance to try/catch blocks in other
languages, but condition handlers are *not* a form of exception handling in the
same manner.
A condition is declared through the `condition!` macro provided by the compiler:
~~~{.rust}
condition! {
pub my_error: int -> ~str;
}
~~~
This macro declares an inner module called `my_error` with one static variable,
`cond` that is a static `Condition` instance. To help understand what the other
parameters are used for, an example usage of this condition would be:
~~~{.rust}
do my_error::cond.trap(|raised_int| {
// the condition `my_error` was raised on, and the value it raised is stored
// in `raised_int`. This closure must return a `~str` type (as specified in
// the declaration of the condition
if raised_int == 3 { ~"three" } else { ~"oh well" }
}).inside {
// The condition handler above is installed for the duration of this block.
// That handler will override any previous handler, but the previous handler
// is restored when this block returns (handlers nest)
//
// If any code from this block (or code from another block) raises on the
// condition, then the above handler will be invoked (so long as there's no
// other nested handler).
println(my_error::cond.raise(3)); // prints "three"
println(my_error::cond.raise(4)); // prints "oh well"
}
~~~
Condition handling is useful in cases where propagating errors is either to
cumbersome or just not necessary in the first place. It should also be noted,
though, that if there is not handler installed when a condition is raised, then
the task invokes `fail!()` and will terminate.
## More Info
Condition handlers as an error strategy is well explained in the [conditions
tutorial](http://static.rust-lang.org/doc/master/tutorial-conditions.html),
along with comparing and contrasting it with other error handling strategies.
*/
use local_data;
use prelude::*;
use unstable::raw::Closure;
// helper for transmutation, shown below.
type RustClosure = (int, int);
#[doc(hidden)]
pub struct Handler<T, U> {
handle: RustClosure,
prev: Option<@Handler<T, U>>,
priv handle: Closure,
priv prev: Option<@Handler<T, U>>,
}
/// This struct represents the state of a condition handler. It contains a key
/// into TLS which holds the currently install handler, along with the name of
/// the condition (useful for debugging).
///
/// This struct should never be created directly, but rather only through the
/// `condition!` macro provided to all libraries using libstd.
pub struct Condition<T, U> {
/// Name of the condition handler
name: &'static str,
/// TLS key used to insert/remove values in TLS.
key: local_data::Key<@Handler<T, U>>
}
impl<T, U> Condition<T, U> {
/// Creates an object which binds the specified handler. This will also save
/// the current handler *on creation* such that when the `Trap` is consumed,
/// it knows which handler to restore.
///
/// # Example
///
/// ~~~{.rust}
/// condition! { my_error: int -> int; }
///
/// let trap = my_error::cond.trap(|error| error + 3);
///
/// // use `trap`'s inside method to register the handler and then run a
/// // block of code with the handler registered
/// ~~~
pub fn trap<'a>(&'a self, h: &'a fn(T) -> U) -> Trap<'a, T, U> {
unsafe {
let p : *RustClosure = ::cast::transmute(&h);
let prev = local_data::get(self.key, |k| k.map(|&x| *x));
let h = @Handler { handle: *p, prev: prev };
Trap { cond: self, handler: h }
}
let h: Closure = unsafe { ::cast::transmute(h) };
let prev = local_data::get(self.key, |k| k.map(|&x| *x));
let h = @Handler { handle: h, prev: prev };
Trap { cond: self, handler: h }
}
/// Raises on this condition, invoking any handler if one has been
/// registered, or failing the current task otherwise.
///
/// While a condition handler is being run, the condition will have no
/// handler listed, so a task failure will occur if the condition is
/// re-raised during the handler.
///
/// # Arguments
///
/// * t - The argument to pass along to the condition handler.
///
/// # Return value
///
/// If a handler is found, its return value is returned, otherwise this
/// function will not return.
pub fn raise(&self, t: T) -> U {
let msg = fmt!("Unhandled condition: %s: %?", self.name, t);
self.raise_default(t, || fail!(msg.clone()))
}
/// Performs the same functionality as `raise`, except that when no handler
/// is found the `default` argument is called instead of failing the task.
pub fn raise_default(&self, t: T, default: &fn() -> U) -> U {
unsafe {
match local_data::pop(self.key) {
None => {
debug!("Condition.raise: found no handler");
default()
}
Some(handler) => {
debug!("Condition.raise: found handler");
match handler.prev {
None => {}
Some(hp) => local_data::set(self.key, hp)
}
let handle : &fn(T) -> U =
::cast::transmute(handler.handle);
let u = handle(t);
local_data::set(self.key, handler);
u
match local_data::pop(self.key) {
None => {
debug!("Condition.raise: found no handler");
default()
}
Some(handler) => {
debug!("Condition.raise: found handler");
match handler.prev {
None => {}
Some(hp) => local_data::set(self.key, hp)
}
let handle : &fn(T) -> U = unsafe {
::cast::transmute(handler.handle)
};
let u = handle(t);
local_data::set(self.key, handler);
u
}
}
}
}
/// A `Trap` is created when the `trap` method is invoked on a `Condition`, and
/// it is used to actually bind a handler into the TLS slot reserved for this
/// condition.
///
/// Normally this object is not dealt with directly, but rather it's directly
/// used after being returned from `trap`
struct Trap<'self, T, U> {
cond: &'self Condition<T, U>,
handler: @Handler<T, U>
priv cond: &'self Condition<T, U>,
priv handler: @Handler<T, U>
}
impl<'self, T, U> Trap<'self, T, U> {
/// Execute a block of code with this trap handler's exception handler
/// registered.
///
/// # Example
///
/// ~~~{.rust}
/// condition! { my_error: int -> int; }
///
/// let result = do my_error::cond.trap(|error| error + 3).inside {
/// my_error::cond.raise(4)
/// };
/// assert_eq!(result, 7);
/// ~~~
pub fn inside<V>(&self, inner: &'self fn() -> V) -> V {
let _g = Guard { cond: self.cond };
debug!("Trap: pushing handler to TLS");
@ -81,8 +189,9 @@ impl<'self, T, U> Trap<'self, T, U> {
}
}
#[doc(hidden)]
struct Guard<'self, T, U> {
cond: &'self Condition<T, U>
priv cond: &'self Condition<T, U>
}
#[unsafe_destructor]

View File

@ -10,17 +10,18 @@
/*!
# The Formatting Module
The Formatting Module
This module contains the runtime support for the `format!` syntax extension. This
macro is implemented in the compiler to emit calls to this module in order to
format arguments at runtime into strings and streams.
This module contains the runtime support for the `format!` syntax extension.
This macro is implemented in the compiler to emit calls to this module in order
to format arguments at runtime into strings and streams.
The functions contained in this module should not normally be used in everyday
use cases of `format!`. The assumptions made by these functions are unsafe for all
inputs, and the compiler performs a large amount of validation on the arguments
to `format!` in order to ensure safety at runtime. While it is possible to call
these functions directly, it is not recommended to do so in the general case.
use cases of `format!`. The assumptions made by these functions are unsafe for
all inputs, and the compiler performs a large amount of validation on the
arguments to `format!` in order to ensure safety at runtime. While it is
possible to call these functions directly, it is not recommended to do so in the
general case.
## Usage

View File

@ -8,12 +8,59 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*! Composable external iterators
/*!
The `Iterator` trait defines an interface for objects which implement iteration as a state machine.
Composable external iterators
Algorithms like `zip` are provided as `Iterator` implementations which wrap other objects
implementing the `Iterator` trait.
# The `Iterator` trait
This module defines Rust's core iteration trait. The `Iterator` trait has one
un-implemented method, `next`. All other methods are derived through default
methods to perform operations such as `zip`, `chain`, `enumerate`, and `fold`.
The goal of this module is to unify iteration across all containers in Rust.
An iterator can be considered as a state machine which is used to track which
element will be yielded next.
There are various extensions also defined in this module to assist with various
types of iteration, such as the `DoubleEndedIterator` for iterating in reverse,
the `FromIterator` trait for creating a container from an iterator, and much
more.
## Rust's `for` loop
The special syntax used by rust's `for` loop is based around the `Iterator`
trait defined in this module. For loops can be viewed as a syntactical expansion
into a `loop`, for example, the `for` loop in this example is essentially
translated to the `loop` below.
~~~{.rust}
let values = ~[1, 2, 3];
// "Syntactical sugar" taking advantage of an iterator
for &x in values.iter() {
println!("{}", x);
}
// Rough translation of the iteration without a `for` iterator.
let mut it = values.iter();
loop {
match it.next() {
Some(&x) => {
println!("{}", x);
}
None => { break }
}
}
~~~
This `for` loop syntax can be applied to any iterator over any type.
## Iteration protocol and more
More detailed information about iterators can be found in the [container
tutorial](http://static.rust-lang.org/doc/master/tutorial-container.html) with
the rest of the rust manuals.
*/

View File

@ -8,12 +8,86 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! String manipulation
//!
//! Strings are a packed UTF-8 representation of text, stored as
//! buffers of u8 bytes. The buffer is not null terminated.
//! Strings should be indexed in bytes, for efficiency, but UTF-8 unsafe
//! operations should be avoided.
/*!
String manipulation
# Basic Usage
Rust's string type is one of the core primitive types of the language. While
represented by the name `str`, the name `str` is not actually a valid type in
Rust. Each string must also be decorated with how its ownership. This means that
there are three common kinds of strings in rust:
* `~str` - This is an owned string. This type obeys all of the normal semantics
of the `~T` types, meaning that it has one, and only one, owner. This
type cannot be implicitly copied, and is moved out of when passed to
other functions.
* `@str` - This is a managed string. Similarly to `@T`, this type can be
implicitly copied, and each implicit copy will increment the
reference count to the string. This means that there is not "true
owner" of the string, and the string will be deallocated when the
reference count reaches 0.
* `&str` - Finally, this is the borrowed string type. This type of string can
only be created from one of the other two kinds of strings. As the
name "borrowed" implies, this type of string is owned elsewhere, and
this string cannot be moved out of.
As an example, here's a few different kinds of strings.
~~~{.rust}
let owned_string = ~"I am an owned string";
let managed_string = @"This string is garbage-collected";
let borrowed_string1 = "This string is borrowed with the 'static lifetime";
let borrowed_string2: &str = owned_string; // owned strings can be borrowed
let borrowed_string3: &str = managed_string; // managed strings can also be borrowed
~~~
From the example above, you can see that rust has 3 different kinds of string
literals. The owned/managed literals correspond to the owned/managed string
types, but the "borrowed literal" is actually more akin to C's concept of a
static string.
When a string is declared without a `~` or `@` sigil, then the string is
allocated statically in the rodata of the executable/library. The string then
has the type `&'static str` meaning that the string is valid for the `'static`
lifetime, otherwise known as the lifetime of the entire program. As can be
inferred from the type, these static strings are not mutable.
# Mutability
Many languages have immutable strings by default, and rust has a particular
flavor on this idea. As with the rest of Rust types, strings are immutable by
default. If a string is declared as `mut`, however, it may be mutated. This
works the same way as the rest of Rust's type system in the sense that if
there's a mutable reference to a string, there may only be one mutable reference
to that string. With these guarantees, strings can easily transition between
being mutable/immutable with the same benefits of having mutable strings in
other languages.
~~~{.rust}
let mut buf = ~"testing";
buf.push_char(' ');
buf.push_str("123");
assert_eq!(buf, ~"testing 123");
~~~
# Representation
Rust's string type, `str`, is a sequence of unicode codepoints encoded as a
stream of UTF-8 bytes. All safely-created strings are guaranteed to be validly
encoded UTF-8 sequences. Additionally, strings are not guaranteed to be
null-terminated (the null byte is a valid unicode codepoint).
The actual representation of strings have direct mappings to vectors:
* `~str` is the same as `~[u8]`
* `&str` is the same as `&[u8]`
* `@str` is the same as `@[u8]`
*/
use at_vec;
use cast;

View File

@ -10,6 +10,8 @@
/*!
Vector manipulation
The `vec` module contains useful code to help work with vector values.
Vectors are Rust's list type. Vectors contain zero or more values of
homogeneous types: