Document a few undocumented modules in libstd
Hopefull this will make our libstd docs appear a little more "full".
This commit is contained in:
parent
0efc4822e9
commit
88bc11e646
6 changed files with 336 additions and 51 deletions
|
@ -8,6 +8,58 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// 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 cast;
|
||||||
use iter::{Iterator, range};
|
use iter::{Iterator, range};
|
||||||
use libc;
|
use libc;
|
||||||
|
|
|
@ -8,71 +8,179 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// 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 local_data;
|
||||||
use prelude::*;
|
use prelude::*;
|
||||||
|
use unstable::raw::Closure;
|
||||||
|
|
||||||
// helper for transmutation, shown below.
|
#[doc(hidden)]
|
||||||
type RustClosure = (int, int);
|
|
||||||
|
|
||||||
pub struct Handler<T, U> {
|
pub struct Handler<T, U> {
|
||||||
handle: RustClosure,
|
priv handle: Closure,
|
||||||
prev: Option<@Handler<T, U>>,
|
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> {
|
pub struct Condition<T, U> {
|
||||||
|
/// Name of the condition handler
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
|
/// TLS key used to insert/remove values in TLS.
|
||||||
key: local_data::Key<@Handler<T, U>>
|
key: local_data::Key<@Handler<T, U>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, U> Condition<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> {
|
pub fn trap<'a>(&'a self, h: &'a fn(T) -> U) -> Trap<'a, T, U> {
|
||||||
unsafe {
|
let h: Closure = unsafe { ::cast::transmute(h) };
|
||||||
let p : *RustClosure = ::cast::transmute(&h);
|
let prev = local_data::get(self.key, |k| k.map(|&x| *x));
|
||||||
let prev = local_data::get(self.key, |k| k.map(|&x| *x));
|
let h = @Handler { handle: h, prev: prev };
|
||||||
let h = @Handler { handle: *p, prev: prev };
|
Trap { cond: self, handler: h }
|
||||||
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 {
|
pub fn raise(&self, t: T) -> U {
|
||||||
let msg = fmt!("Unhandled condition: %s: %?", self.name, t);
|
let msg = fmt!("Unhandled condition: %s: %?", self.name, t);
|
||||||
self.raise_default(t, || fail!(msg.clone()))
|
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 {
|
pub fn raise_default(&self, t: T, default: &fn() -> U) -> U {
|
||||||
unsafe {
|
match local_data::pop(self.key) {
|
||||||
match local_data::pop(self.key) {
|
None => {
|
||||||
None => {
|
debug!("Condition.raise: found no handler");
|
||||||
debug!("Condition.raise: found no handler");
|
default()
|
||||||
default()
|
}
|
||||||
}
|
Some(handler) => {
|
||||||
Some(handler) => {
|
debug!("Condition.raise: found handler");
|
||||||
debug!("Condition.raise: found handler");
|
match handler.prev {
|
||||||
match handler.prev {
|
None => {}
|
||||||
None => {}
|
Some(hp) => local_data::set(self.key, hp)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
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> {
|
struct Trap<'self, T, U> {
|
||||||
cond: &'self Condition<T, U>,
|
priv cond: &'self Condition<T, U>,
|
||||||
handler: @Handler<T, U>
|
priv handler: @Handler<T, U>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'self, T, U> Trap<'self, 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 {
|
pub fn inside<V>(&self, inner: &'self fn() -> V) -> V {
|
||||||
let _g = Guard { cond: self.cond };
|
let _g = Guard { cond: self.cond };
|
||||||
debug!("Trap: pushing handler to TLS");
|
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> {
|
struct Guard<'self, T, U> {
|
||||||
cond: &'self Condition<T, U>
|
priv cond: &'self Condition<T, U>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe_destructor]
|
#[unsafe_destructor]
|
||||||
|
|
|
@ -10,17 +10,18 @@
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|
||||||
# The Formatting Module
|
The Formatting Module
|
||||||
|
|
||||||
This module contains the runtime support for the `format!` syntax extension. This
|
This module contains the runtime support for the `format!` syntax extension.
|
||||||
macro is implemented in the compiler to emit calls to this module in order to
|
This macro is implemented in the compiler to emit calls to this module in order
|
||||||
format arguments at runtime into strings and streams.
|
to format arguments at runtime into strings and streams.
|
||||||
|
|
||||||
The functions contained in this module should not normally be used in everyday
|
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
|
use cases of `format!`. The assumptions made by these functions are unsafe for
|
||||||
inputs, and the compiler performs a large amount of validation on the arguments
|
all inputs, and the compiler performs a large amount of validation on the
|
||||||
to `format!` in order to ensure safety at runtime. While it is possible to call
|
arguments to `format!` in order to ensure safety at runtime. While it is
|
||||||
these functions directly, it is not recommended to do so in the general case.
|
possible to call these functions directly, it is not recommended to do so in the
|
||||||
|
general case.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
|
|
@ -8,12 +8,59 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// 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
|
# The `Iterator` trait
|
||||||
implementing 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.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -8,12 +8,86 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
//! String manipulation
|
/*!
|
||||||
//!
|
|
||||||
//! Strings are a packed UTF-8 representation of text, stored as
|
String manipulation
|
||||||
//! buffers of u8 bytes. The buffer is not null terminated.
|
|
||||||
//! Strings should be indexed in bytes, for efficiency, but UTF-8 unsafe
|
# Basic Usage
|
||||||
//! operations should be avoided.
|
|
||||||
|
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 at_vec;
|
||||||
use cast;
|
use cast;
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|
||||||
|
Vector manipulation
|
||||||
|
|
||||||
The `vec` module contains useful code to help work with vector values.
|
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
|
Vectors are Rust's list type. Vectors contain zero or more values of
|
||||||
homogeneous types:
|
homogeneous types:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue