rollup merge of #22331: steveklabnik/guidelines

Fixes #19315

r? @aturon
This commit is contained in:
Alex Crichton 2015-02-17 15:14:14 -08:00
commit 25ccf3c0da
58 changed files with 2554 additions and 1 deletions

View File

@ -73,7 +73,7 @@ RUSTBOOK = $(RPATH_VAR2_T_$(CFG_BUILD)_H_$(CFG_BUILD)) $(RUSTBOOK_EXE)
D := $(S)src/doc
DOC_TARGETS := trpl
DOC_TARGETS := trpl style
COMPILER_DOC_TARGETS :=
DOC_L10N_TARGETS :=
@ -275,3 +275,9 @@ trpl: doc/book/index.html
doc/book/index.html: $(RUSTBOOK_EXE) $(wildcard $(S)/src/doc/trpl/*.md) | doc/
$(Q)rm -rf doc/book
$(Q)$(RUSTBOOK) build $(S)src/doc/trpl doc/book
style: doc/style/index.html
doc/style/index.html: $(RUSTBOOK_EXE) $(wildcard $(S)/src/doc/style/*.md) | doc/
$(Q)rm -rf doc/style
$(Q)$(RUSTBOOK) build $(S)src/doc/style doc/style

64
src/doc/style/README.md Normal file
View File

@ -0,0 +1,64 @@
% Style Guidelines
This document collects the emerging principles, conventions, abstractions, and
best practices for writing Rust code.
Since Rust is evolving at a rapid pace, these guidelines are
preliminary. The hope is that writing them down explicitly will help
drive discussion, consensus and adoption.
Whenever feasible, guidelines provide specific examples from Rust's standard
libraries.
### Guideline statuses
Every guideline has a status:
* **[FIXME]**: Marks places where there is more work to be done. In
some cases, that just means going through the RFC process.
* **[FIXME #NNNNN]**: Like **[FIXME]**, but links to the issue tracker.
* **[RFC #NNNN]**: Marks accepted guidelines, linking to the rust-lang
RFC establishing them.
### Guideline stabilization
One purpose of these guidelines is to reach decisions on a number of
cross-cutting API and stylistic choices. Discussion and development of
the guidelines will happen primarily on http://discuss.rust-lang.org/,
using the Guidelines category. Discussion can also occur on the
[guidelines issue tracker](https://github.com/rust-lang/rust-guidelines).
Guidelines that are under development or discussion will be marked with the
status **[FIXME]**, with a link to the issue tracker when appropriate.
Once a concrete guideline is ready to be proposed, it should be filed
as an [FIXME: needs RFC](https://github.com/rust-lang/rfcs). If the RFC is
accepted, the official guidelines will be updated to match, and will
include the tag **[RFC #NNNN]** linking to the RFC document.
### What's in this document
This document is broken into four parts:
* **[Style](style/README.md)** provides a set of rules governing naming conventions,
whitespace, and other stylistic issues.
* **[Guidelines by Rust feature](features/README.md)** places the focus on each of
Rust's features, starting from expressions and working the way out toward
crates, dispensing guidelines relevant to each.
* **Topical guidelines and patterns**. The rest of the document proceeds by
cross-cutting topic, starting with
[Ownership and resources](ownership/README.md).
* **[APIs for a changing Rust](changing/README.md)**
discusses the forward-compatibility hazards, especially those that interact
with the pre-1.0 library stabilization process.
> **[FIXME]** Add cross-references throughout this document to the tutorial,
> reference manual, and other guides.
> **[FIXME]** What are some _non_-goals, _non_-principles, or _anti_-patterns that
> we should document?

54
src/doc/style/SUMMARY.md Normal file
View File

@ -0,0 +1,54 @@
# Summary
* [Style](style/README.md)
* [Whitespace](style/whitespace.md)
* [Comments](style/comments.md)
* [Braces, semicolons, commas](style/braces.md)
* [Naming](style/naming/README.md)
* [Ownership variants](style/naming/ownership.md)
* [Containers/wrappers](style/naming/containers.md)
* [Conversions](style/naming/conversions.md)
* [Iterators](style/naming/iterators.md)
* [Imports](style/imports.md)
* [Organization](style/organization.md)
* [Guidelines by Rust feature](features/README.md)
* [Let binding](features/let.md)
* [Pattern matching](features/match.md)
* [Loops](features/loops.md)
* [Functions and methods](features/functions-and-methods/README.md)
* [Input](features/functions-and-methods/input.md)
* [Output](features/functions-and-methods/output.md)
* [For convenience](features/functions-and-methods/convenience.md)
* [Types](features/types/README.md)
* [Conversions](features/types/conversions.md)
* [The newtype pattern](features/types/newtype.md)
* [Traits](features/traits/README.md)
* [For generics](features/traits/generics.md)
* [For objects](features/traits/objects.md)
* [For overloading](features/traits/overloading.md)
* [For extensions](features/traits/extensions.md)
* [For reuse](features/traits/reuse.md)
* [Common traits](features/traits/common.md)
* [Modules](features/modules.md)
* [Crates](features/crates.md)
* [Ownership and resources](ownership/README.md)
* [Constructors](ownership/constructors.md)
* [Builders](ownership/builders.md)
* [Destructors](ownership/destructors.md)
* [RAII](ownership/raii.md)
* [Cells and smart pointers](ownership/cell-smart.md)
* [Errors](errors/README.md)
* [Signaling](errors/signaling.md)
* [Handling](errors/handling.md)
* [Propagation](errors/propagation.md)
* [Ergonomics](errors/ergonomics.md)
* [Safety and guarantees](safety/README.md)
* [Using unsafe](safety/unsafe.md)
* [Library guarantees](safety/lib-guarantees.md)
* [Testing](testing/README.md)
* [Unit testing](testing/unit.md)
* [FFI, platform-specific code](platform.md)
* [APIs for a changing Rust](changing/README.md)
* [Pre-1.0 changes](changing/pre-1-0.md)
* [Post-1.0 changes](changing/post-1-0.md)
* [Timing unclear](changing/unclear.md)

View File

@ -0,0 +1,5 @@
% API design for a changing Rust
A number of planned Rust features will drastically affect the API design
story. This section collects some of the biggest features with concrete examples
of how the API will change.

View File

@ -0,0 +1,12 @@
% Post-1.0 changes
### Higher-kinded types
* A trait encompassing both `Iterable<T>` for some fixed `T` and
`FromIterator<U>` for _all_ `U` (where HKT comes in). The train
could provide e.g. a default `map` method producing the same kind of
the container, but with a new type parameter.
* **Monadic-generic programming**? Can we add this without deprecating
huge swaths of the API (including `Option::map`, `option::collect`,
`result::collect`, `try!` etc.

View File

@ -0,0 +1,17 @@
% Pre-1.0 changes
### `std` facade
We should revisit some APIs in `libstd` now that the facade effort is complete.
For example, the treatment of environment variables in the new
`Command` API is waiting on access to hashtables before being
approved.
### Trait reform
Potential for standard conversion traits (`to`, `into`, `as`).
Probably many other opportunities here.
### Unboxed closures

View File

@ -0,0 +1,28 @@
% Changes with unclear timing
### Associated items
* Many traits that currently take type parameters should instead use associated
types; this will _drastically_ simplify signatures in some cases.
* Associated constants would be useful in a few places, e.g. traits for
numerics, traits for paths.
### Anonymous, unboxed return types (aka `impl Trait` types)
* See https://github.com/rust-lang/rfcs/pull/105
* Could affect API design in several places, e.g. the `Iterator` trait.
### Default type parameters
We are already using this in a few places (e.g. `HashMap`), but it's
feature-gated.
### Compile-time function evaluation (CTFE)
https://github.com/mozilla/rust/issues/11621
### Improved constant folding
https://github.com/rust-lang/rust/issues/7834

View File

@ -0,0 +1,3 @@
% Errors
> **[FIXME]** Add some general text here.

View File

@ -0,0 +1,66 @@
% Ergonomic error handling
Error propagation with raw `Result`s can require tedious matching and
repackaging. This tedium is largely alleviated by the `try!` macro,
and can be completely removed (in some cases) by the "`Result`-`impl`"
pattern.
### The `try!` macro
Prefer
```rust
use std::io::{File, Open, Write, IoError};
struct Info {
name: String,
age: int,
rating: int
}
fn write_info(info: &Info) -> Result<(), IoError> {
let mut file = File::open_mode(&Path::new("my_best_friends.txt"),
Open, Write);
// Early return on error
try!(file.write_line(format!("name: {}", info.name).as_slice()));
try!(file.write_line(format!("age: {}", info.age).as_slice()));
try!(file.write_line(format!("rating: {}", info.rating).as_slice()));
return Ok(());
}
```
over
```rust
use std::io::{File, Open, Write, IoError};
struct Info {
name: String,
age: int,
rating: int
}
fn write_info(info: &Info) -> Result<(), IoError> {
let mut file = File::open_mode(&Path::new("my_best_friends.txt"),
Open, Write);
// Early return on error
match file.write_line(format!("name: {}", info.name).as_slice()) {
Ok(_) => (),
Err(e) => return Err(e)
}
match file.write_line(format!("age: {}", info.age).as_slice()) {
Ok(_) => (),
Err(e) => return Err(e)
}
return file.write_line(format!("rating: {}", info.rating).as_slice());
}
```
See
[the `result` module documentation](http://static.rust-lang.org/doc/master/std/result/index.html#the-try!-macro)
for more details.
### The `Result`-`impl` pattern [FIXME]
> **[FIXME]** Document the way that the `io` module uses trait impls
> on `IoResult` to painlessly propagate errors.

View File

@ -0,0 +1,7 @@
% Handling errors
### Use task isolation to cope with failure. [FIXME]
> **[FIXME]** Explain how to isolate tasks and detect task failure for recovery.
### Consuming `Result` [FIXME]

View File

@ -0,0 +1,8 @@
% Propagation
> **[FIXME]** We need guidelines on how to layer error information up a stack of
> abstractions.
### Error interoperation [FIXME]
> **[FIXME]** Document the `FromError` infrastructure.

View File

@ -0,0 +1,125 @@
% Signaling errors [RFC #236]
> The guidelines below were approved by [RFC #236](https://github.com/rust-lang/rfcs/pull/236).
Errors fall into one of three categories:
* Catastrophic errors, e.g. out-of-memory.
* Contract violations, e.g. wrong input encoding, index out of bounds.
* Obstructions, e.g. file not found, parse error.
The basic principle of the convention is that:
* Catastrophic errors and programming errors (bugs) can and should only be
recovered at a *coarse grain*, i.e. a task boundary.
* Obstructions preventing an operation should be reported at a maximally *fine
grain* -- to the immediate invoker of the operation.
## Catastrophic errors
An error is _catastrophic_ if there is no meaningful way for the current task to
continue after the error occurs.
Catastrophic errors are _extremely_ rare, especially outside of `libstd`.
**Canonical examples**: out of memory, stack overflow.
### For catastrophic errors, panic
For errors like stack overflow, Rust currently aborts the process, but
could in principle panic, which (in the best case) would allow
reporting and recovery from a supervisory task.
## Contract violations
An API may define a contract that goes beyond the type checking enforced by the
compiler. For example, slices support an indexing operation, with the contract
that the supplied index must be in bounds.
Contracts can be complex and involve more than a single function invocation. For
example, the `RefCell` type requires that `borrow_mut` not be called until all
existing borrows have been relinquished.
### For contract violations, panic
A contract violation is always a bug, and for bugs we follow the Erlang
philosophy of "let it crash": we assume that software *will* have bugs, and we
design coarse-grained task boundaries to report, and perhaps recover, from these
bugs.
### Contract design
One subtle aspect of these guidelines is that the contract for a function is
chosen by an API designer -- and so the designer also determines what counts as
a violation.
This RFC does not attempt to give hard-and-fast rules for designing
contracts. However, here are some rough guidelines:
* Prefer expressing contracts through static types whenever possible.
* It *must* be possible to write code that uses the API without violating the
contract.
* Contracts are most justified when violations are *inarguably* bugs -- but this
is surprisingly rare.
* Consider whether the API client could benefit from the contract-checking
logic. The checks may be expensive. Or there may be useful programming
patterns where the client does not want to check inputs before hand, but would
rather attempt the operation and then find out whether the inputs were invalid.
* When a contract violation is the *only* kind of error a function may encounter
-- i.e., there are no obstructions to its success other than "bad" inputs --
using `Result` or `Option` instead is especially warranted. Clients can then use
`unwrap` to assert that they have passed valid input, or re-use the error
checking done by the API for their own purposes.
* When in doubt, use loose contracts and instead return a `Result` or `Option`.
## Obstructions
An operation is *obstructed* if it cannot be completed for some reason, even
though the operation's contract has been satisfied. Obstructed operations may
have (documented!) side effects -- they are not required to roll back after
encountering an obstruction. However, they should leave the data structures in
a "coherent" state (satisfying their invariants, continuing to guarantee safety,
etc.).
Obstructions may involve external conditions (e.g., I/O), or they may involve
aspects of the input that are not covered by the contract.
**Canonical examples**: file not found, parse error.
### For obstructions, use `Result`
The
[`Result<T,E>` type](http://static.rust-lang.org/doc/master/std/result/index.html)
represents either a success (yielding `T`) or failure (yielding `E`). By
returning a `Result`, a function allows its clients to discover and react to
obstructions in a fine-grained way.
#### What about `Option`?
The `Option` type should not be used for "obstructed" operations; it
should only be used when a `None` return value could be considered a
"successful" execution of the operation.
This is of course a somewhat subjective question, but a good litmus
test is: would a reasonable client ever ignore the result? The
`Result` type provides a lint that ensures the result is actually
inspected, while `Option` does not, and this difference of behavior
can help when deciding between the two types.
Another litmus test: can the operation be understood as asking a
question (possibly with sideeffects)? Operations like `pop` on a
vector can be viewed as asking for the contents of the first element,
with the side effect of removing it if it exists -- with an `Option`
return value.
## Do not provide both `Result` and `panic!` variants.
An API should not provide both `Result`-producing and `panic`king versions of an
operation. It should provide just the `Result` version, allowing clients to use
`try!` or `unwrap` instead as needed. This is part of the general pattern of
cutting down on redundant variants by instead using method chaining.

View File

@ -0,0 +1,9 @@
% Guidelines by language feature
Rust provides a unique combination of language features, some new and some
old. This section gives guidance on when and how to use Rust's features, and
brings attention to some of the tradeoffs between different features.
Notably missing from this section is an in-depth discussion of Rust's pointer
types (both built-in and in the library). The topic of pointers is discussed at
length in a [separate section on ownership](../ownership/README.md).

View File

@ -0,0 +1,6 @@
% Crates
> **[FIXME]** What general guidelines should we provide for crate design?
> Possible topics: facades; per-crate preludes (to be imported as globs);
> "lib.rs"

View File

@ -0,0 +1,43 @@
% Functions and methods
### Prefer methods to functions if there is a clear receiver. **[FIXME: needs RFC]**
Prefer
```rust
impl Foo {
pub fn frob(&self, w: widget) { ... }
}
```
over
```rust
pub fn frob(foo: &Foo, w: widget) { ... }
```
for any operation that is clearly associated with a particular
type.
Methods have numerous advantages over functions:
* They do not need to be imported or qualified to be used: all you
need is a value of the appropriate type.
* Their invocation performs autoborrowing (including mutable borrows).
* They make it easy to answer the question "what can I do with a value
of type `T`" (especially when using rustdoc).
* They provide `self` notation, which is more concise and often more
clearly conveys ownership distinctions.
> **[FIXME]** Revisit these guidelines with
> [UFCS](https://github.com/nick29581/rfcs/blob/ufcs/0000-ufcs.md) and
> conventions developing around it.
### Guidelines for inherent methods. **[FIXME]**
> **[FIXME]** We need guidelines for when to provide inherent methods on a type,
> versus methods through a trait or functions.
> **NOTE**: Rules for method resolution around inherent methods are in flux,
> which may impact the guidelines.

View File

@ -0,0 +1,43 @@
% Convenience methods
### Provide small, coherent sets of convenience methods. **[FIXME: needs RFC]**
_Convenience methods_ wrap up existing functionality in a more convenient
way. The work done by a convenience method varies widely:
* _Re-providing functions as methods_. For example, the `std::path::Path` type
provides methods like `stat` on `Path`s that simply invoke the corresponding
function in `std::io::fs`.
* _Skipping through conversions_. For example, the `str` type provides a
`.len()` convenience method which is also expressible as `.as_bytes().len()`.
Sometimes the conversion is more complex: the `str` module also provides
`from_chars`, which encapsulates a simple use of iterators.
* _Encapsulating common arguments_. For example, vectors of `&str`s
provide a `connect` as well as a special case, `concat`, that is expressible
using `connect` with a fixed separator of `""`.
* _Providing more efficient special cases_. The `connect` and `concat` example
also applies here: singling out `concat` as a special case allows for a more
efficient implementation.
Note, however, that the `connect` method actually detects the special case
internally and invokes `concat`. Usually, it is not necessary to add a public
convenience method just for efficiency gains; there should also be a
_conceptual_ reason to add it, e.g. because it is such a common special case.
It is tempting to add convenience methods in a one-off, haphazard way as
common use patterns emerge. Avoid this temptation, and instead _design_ small,
coherent sets of convenience methods that are easy to remember:
* _Small_: Avoid combinatorial explosions of convenience methods. For example,
instead of adding `_str` variants of methods that provide a `str` output,
instead ensure that the normal output type of methods is easily convertible to
`str`.
* _Coherent_: Look for small groups of convenience methods that make sense to
include together. For example, the `Path` API mentioned above includes a small
selection of the most common filesystem operations that take a `Path`
argument. If one convenience method strongly suggests the existence of others,
consider adding the whole group.
* _Memorable_: It is not worth saving a few characters of typing if you have to
look up the name of a convenience method every time you use it. Add
convenience methods with names that are obvious and easy to remember, and add
them for the most common or painful use cases.

View File

@ -0,0 +1,201 @@
% Input to functions and methods
### Let the client decide when to copy and where to place data. [FIXME: needs RFC]
#### Copying:
Prefer
```rust
fn foo(b: Bar) {
// use b as owned, directly
}
```
over
```rust
fn foo(b: &Bar) {
let b = b.clone();
// use b as owned after cloning
}
```
If a function requires ownership of a value of unknown type `T`, but does not
otherwise need to make copies, the function should take ownership of the
argument (pass by value `T`) rather than using `.clone()`. That way, the caller
can decide whether to relinquish ownership or to `clone`.
Similarly, the `Copy` trait bound should only be demanded it when absolutely
needed, not as a way of signaling that copies should be cheap to make.
#### Placement:
Prefer
```rust
fn foo(b: Bar) -> Bar { ... }
```
over
```rust
fn foo(b: Box<Bar>) -> Box<Bar> { ... }
```
for concrete types `Bar` (as opposed to trait objects). This way, the caller can
decide whether to place data on the stack or heap. No overhead is imposed by
letting the caller determine the placement.
### Minimize assumptions about parameters. [FIXME: needs RFC]
The fewer assumptions a function makes about its inputs, the more widely usable
it becomes.
#### Minimizing assumptions through generics:
Prefer
```rust
fn foo<T: Iterator<int>>(c: T) { ... }
```
over any of
```rust
fn foo(c: &[int]) { ... }
fn foo(c: &Vec<int>) { ... }
fn foo(c: &SomeOtherCollection<int>) { ... }
```
if the function only needs to iterate over the data.
More generally, consider using generics to pinpoint the assumptions a function
needs to make about its arguments.
On the other hand, generics can make it more difficult to read and understand a
function's signature. Aim for "natural" parameter types that a neither overly
concrete nor overly abstract. See the discussion on
[traits](../../traits/README.md) for more guidance.
#### Minimizing ownership assumptions:
Prefer either of
```rust
fn foo(b: &Bar) { ... }
fn foo(b: &mut Bar) { ... }
```
over
```rust
fn foo(b: Bar) { ... }
```
That is, prefer borrowing arguments rather than transferring ownership, unless
ownership is actually needed.
### Prefer compound return types to out-parameters. [FIXME: needs RFC]
Prefer
```rust
fn foo() -> (Bar, Bar)
```
over
```rust
fn foo(output: &mut Bar) -> Bar
```
for returning multiple `Bar` values.
Compound return types like tuples and structs are efficiently compiled
and do not require heap allocation. If a function needs to return
multiple values, it should do so via one of these types.
The primary exception: sometimes a function is meant to modify data
that the caller already owns, for example to re-use a buffer:
```rust
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint>
```
(From the [Reader trait](http://static.rust-lang.org/doc/master/std/io/trait.Reader.html#tymethod.read).)
### Consider validating arguments, statically or dynamically. [FIXME: needs RFC]
_Note: this material is closely related to
[library-level guarantees](../../safety/lib-guarantees.md)._
Rust APIs do _not_ generally follow the
[robustness principle](http://en.wikipedia.org/wiki/Robustness_principle): "be
conservative in what you send; be liberal in what you accept".
Instead, Rust code should _enforce_ the validity of input whenever practical.
Enforcement can be achieved through the following mechanisms (listed
in order of preference).
#### Static enforcement:
Choose an argument type that rules out bad inputs.
For example, prefer
```rust
fn foo(a: ascii::Ascii) { ... }
```
over
```rust
fn foo(a: u8) { ... }
```
Note that
[`ascii::Ascii`](http://static.rust-lang.org/doc/master/std/ascii/struct.Ascii.html)
is a _wrapper_ around `u8` that guarantees the highest bit is zero; see
[newtype patterns]() for more details on creating typesafe wrappers.
Static enforcement usually comes at little run-time cost: it pushes the
costs to the boundaries (e.g. when a `u8` is first converted into an
`Ascii`). It also catches bugs early, during compilation, rather than through
run-time failures.
On the other hand, some properties are difficult or impossible to
express using types.
#### Dynamic enforcement:
Validate the input as it is processed (or ahead of time, if necessary). Dynamic
checking is often easier to implement than static checking, but has several
downsides:
1. Runtime overhead (unless checking can be done as part of processing the input).
2. Delayed detection of bugs.
3. Introduces failure cases, either via `fail!` or `Result`/`Option` types (see
the [error handling guidelines](../../errors/README.md)), which must then be
dealt with by client code.
#### Dynamic enforcement with `debug_assert!`:
Same as dynamic enforcement, but with the possibility of easily turning off
expensive checks for production builds.
#### Dynamic enforcement with opt-out:
Same as dynamic enforcement, but adds sibling functions that opt out of the
checking.
The convention is to mark these opt-out functions with a suffix like
`_unchecked` or by placing them in a `raw` submodule.
The unchecked functions can be used judiciously in cases where (1) performance
dictates avoiding checks and (2) the client is otherwise confident that the
inputs are valid.
> **[FIXME]** Should opt-out functions be marked `unsafe`?

View File

@ -0,0 +1,56 @@
% Output from functions and methods
### Don't overpromise. [FIXME]
> **[FIXME]** Add discussion of overly-specific return types,
> e.g. returning a compound iterator type rather than hiding it behind
> a use of newtype.
### Let clients choose what to throw away. [FIXME: needs RFC]
#### Return useful intermediate results:
Many functions that answer a question also compute interesting related data. If
this data is potentially of interest to the client, consider exposing it in the
API.
Prefer
```rust
struct SearchResult {
found: bool, // item in container?
expected_index: uint // what would the item's index be?
}
fn binary_search(&self, k: Key) -> SearchResult
```
or
```rust
fn binary_search(&self, k: Key) -> (bool, uint)
```
over
```rust
fn binary_search(&self, k: Key) -> bool
```
#### Yield back ownership:
Prefer
```rust
fn from_utf8_owned(vv: Vec<u8>) -> Result<String, Vec<u8>>
```
over
```rust
fn from_utf8_owned(vv: Vec<u8>) -> Option<String>
```
The `from_utf8_owned` function gains ownership of a vector. In the successful
case, the function consumes its input, returning an owned string without
allocating or copying. In the unsuccessful case, however, the function returns
back ownership of the original slice.

View File

@ -0,0 +1,103 @@
% Let binding
### Always separately bind RAII guards. [FIXME: needs RFC]
Prefer
```rust
fn use_mutex(m: sync::mutex::Mutex<int>) {
let guard = m.lock();
do_work(guard);
drop(guard); // unlock the lock
// do other work
}
```
over
```rust
fn use_mutex(m: sync::mutex::Mutex<int>) {
do_work(m.lock());
// do other work
}
```
As explained in the [RAII guide](../ownership/raii.md), RAII guards are values
that represent ownership of some resource and whose destructor releases the
resource. Because the lifetime of guards are significant, they should always be
explicitly `let`-bound to make the lifetime clear. Consider using an explicit
`drop` to release the resource early.
### Prefer conditional expressions to deferred initialization. [FIXME: needs RFC]
Prefer
```rust
let foo = match bar {
Baz => 0,
Quux => 1
};
```
over
```rust
let foo;
match bar {
Baz => {
foo = 0;
}
Quux => {
foo = 1;
}
}
```
unless the conditions for initialization are too complex to fit into a simple
conditional expression.
### Use type annotations for clarification; prefer explicit generics when inference fails. [FIXME: needs RFC]
Prefer
```rust
s.iter().map(|x| x * 2)
.collect::<Vec<_>>()
```
over
```rust
let v: Vec<_> = s.iter().map(|x| x * 2)
.collect();
```
When the type of a value might be unclear to the _reader_ of the code, consider
explicitly annotating it in a `let`.
On the other hand, when the type is unclear to the _compiler_, prefer to specify
the type by explicit generics instantiation, which is usually more clear.
### Shadowing [FIXME]
> **[FIXME]** Repeatedly shadowing a binding is somewhat common in Rust code. We
> need to articulate a guideline on when it is appropriate/useful and when not.
### Prefer immutable bindings. [FIXME: needs RFC]
Use `mut` bindings to signal the span during which a value is mutated:
```rust
let mut v = Vec::new();
// push things onto v
let v = v;
// use v immutably henceforth
```
### Prefer to bind all `struct` or tuple fields. [FIXME: needs RFC]
When consuming a `struct` or tuple via a `let`, bind all of the fields rather
than using `..` to elide the ones you don't need. The benefit is that when
fields are added, the compiler will pinpoint all of the places where that type
of value was consumed, which will often need to be adjusted to take the new
field properly into account.

View File

@ -0,0 +1,13 @@
% Loops
### Prefer `for` to `while`. [FIXME: needs RFC]
A `for` loop is preferable to a `while` loop, unless the loop counts in a
non-uniform way (making it difficult to express using `for`).
### Guidelines for `loop`. [FIXME]
> **[FIXME]** When is `loop` recommended? Some possibilities:
> * For optimistic retry algorithms
> * For servers
> * To avoid mutating local variables sometimes needed to fit `while`

View File

@ -0,0 +1,26 @@
% Pattern matching
### Dereference `match` targets when possible. [FIXME: needs RFC]
Prefer
~~~~
match *foo {
X(...) => ...
Y(...) => ...
}
~~~~
over
~~~~
match foo {
box X(...) => ...
box Y(...) => ...
}
~~~~
<!-- ### Clearly indicate important scopes. **[FIXME: needs RFC]** -->
<!-- If it is important that the destructor for a value be executed at a specific -->
<!-- time, clearly bind that value using a standalone `let` -->

View File

@ -0,0 +1,133 @@
% Modules
> **[FIXME]** What general guidelines should we provide for module design?
> We should discuss visibility, nesting, `mod.rs`, and any interesting patterns
> around modules.
### Headers [FIXME: needs RFC]
Organize module headers as follows:
1. [Imports](../style/imports.md).
1. `mod` declarations.
1. `pub mod` declarations.
### Avoid `path` directives. [FIXME: needs RFC]
Avoid using `#[path="..."]` directives; make the file system and
module hierarchy match, instead.
### Use the module hirearchy to organize APIs into coherent sections. [FIXME]
> **[FIXME]** Flesh this out with examples; explain what a "coherent
> section" is with examples.
>
> The module hirearchy defines both the public and internal API of your module.
> Breaking related functionality into submodules makes it understandable to both
> users and contributors to the module.
### Place modules in their own file. [FIXME: needs RFC]
> **[FIXME]**
> - "<100 lines" is arbitrary, but it's a clearer recommendation
> than "~1 page" or similar suggestions that vary by screen size, etc.
For all except very short modules (<100 lines) and [tests](../testing/README.md),
place the module `foo` in a separate file, as in:
```rust
pub mod foo;
// in foo.rs or foo/mod.rs
pub fn bar() { println!("..."); }
/* ... */
```
rather than declaring it inline:
```rust
pub mod foo {
pub fn bar() { println!("..."); }
/* ... */
}
```
#### Use subdirectories for modules with children. [FIXME: needs RFC]
For modules that themselves have submodules, place the module in a separate
directory (e.g., `bar/mod.rs` for a module `bar`) rather than the same directory.
Note the structure of
[`std::io`](http://doc.rust-lang.org/std/io/). Many of the submodules lack
children, like
[`io::fs`](http://doc.rust-lang.org/std/io/fs/)
and
[`io::stdio`](http://doc.rust-lang.org/std/io/stdio/).
On the other hand,
[`io::net`](http://doc.rust-lang.org/std/io/net/)
contains submodules, so it lives in a separate directory:
```
io/mod.rs
io/extensions.rs
io/fs.rs
io/net/mod.rs
io/net/addrinfo.rs
io/net/ip.rs
io/net/tcp.rs
io/net/udp.rs
io/net/unix.rs
io/pipe.rs
...
```
While it is possible to define all of `io` within a single directory,
mirroring the module hirearchy in the directory structure makes
submodules of `io::net` easier to find.
### Consider top-level definitions or reexports. [FIXME: needs RFC]
For modules with submodules,
define or [reexport](http://doc.rust-lang.org/std/io/#reexports) commonly used
definitions at the top level:
* Functionality relevant to the module itself or to many of its
children should be defined in `mod.rs`.
* Functionality specific to a submodule should live in that
submodule. Reexport at the top level for the most important or
common definitions.
For example,
[`IoError`](http://doc.rust-lang.org/std/io/struct.IoError.html)
is defined in `io/mod.rs`, since it pertains to the entirety of `io`,
while
[`TcpStream`](http://doc.rust-lang.org/std/io/net/tcp/struct.TcpStream.html)
is defined in `io/net/tcp.rs` and reexported in the `io` module.
### Use internal module hirearchies for organization. [FIXME: needs RFC]
> **[FIXME]**
> - Referencing internal modules from the standard library is subject to
> becoming outdated.
Internal module hirearchies (i.e., private submodules) may be used to
hide implementation details that are not part of the module's API.
For example, in [`std::io`](http://doc.rust-lang.org/std/io/), `mod mem`
provides implementations for
[`BufReader`](http://doc.rust-lang.org/std/io/struct.BufReader.html)
and
[`BufWriter`](http://doc.rust-lang.org/std/io/struct.BufWriter.html),
but these are re-exported in `io/mod.rs` at the top level of the module:
```rust
// libstd/io/mod.rs
pub use self::mem::{MemReader, BufReader, MemWriter, BufWriter};
/* ... */
mod mem;
```
This hides the detail that there even exists a `mod mem` in `io`, and
helps keep code organized while offering freedom to change the
implementation.

View File

@ -0,0 +1,22 @@
% Traits
Traits are probably Rust's most complex feature, supporting a wide range of use
cases and design tradeoffs. Patterns of trait usage are still emerging.
### Know whether a trait will be used as an object. [FIXME: needs RFC]
Trait objects have some [significant limitations](objects.md): methods
invoked through a trait object cannot use generics, and cannot use
`Self` except in receiver position.
When designing a trait, decide early on whether the trait will be used
as an [object](objects.md) or as a [bound on generics](generics.md);
the tradeoffs are discussed in each of the linked sections.
If a trait is meant to be used as an object, its methods should take
and return trait objects rather than use generics.
### Default methods [FIXME]
> **[FIXME]** Guidelines for default methods.

View File

@ -0,0 +1,71 @@
% Common traits
### Eagerly implement common traits. [FIXME: needs RFC]
Rust's trait system does not allow _orphans_: roughly, every `impl` must live
either in the crate that defines the trait or the implementing
type. Consequently, crates that define new types should eagerly implement all
applicable, common traits.
To see why, consider the following situation:
* Crate `std` defines trait `Show`.
* Crate `url` defines type `Url`, without implementing `Show`.
* Crate `webapp` imports from both `std` and `url`,
There is no way for `webapp` to add `Show` to `url`, since it defines neither.
(Note: the newtype pattern can provide an efficient, but inconvenient
workaround; see [newtype for views](../types/newtype.md))
The most important common traits to implement from `std` are:
```rust
Clone, Show, Hash, Eq
```
#### When safe, derive or otherwise implement `Send` and `Share`. [FIXME]
> **[FIXME]**. This guideline is in flux while the "opt-in" nature of
> built-in traits is being decided. See https://github.com/rust-lang/rfcs/pull/127
### Prefer to derive, rather than implement. [FIXME: needs RFC]
Deriving saves implementation effort, makes correctness trivial, and
automatically adapts to upstream changes.
### Do not overload operators in surprising ways. [FIXME: needs RFC]
Operators with built in syntax (`*`, `|`, and so on) can be provided for a type
by implementing the traits in `core::ops`. These operators come with strong
expectations: implement `Mul` only for an operation that bears some resemblance
to multiplication (and shares the expected properties, e.g. associativity), and
so on for the other traits.
### The `Drop` trait
The `Drop` trait is treated specially by the compiler as a way of
associating destructors with types. See
[the section on destructors](../../ownership/destructors.md) for
guidance.
### The `Deref`/`DerefMut` traits
#### Use `Deref`/`DerefMut` only for smart pointers. [FIXME: needs RFC]
The `Deref` traits are used implicitly by the compiler in many circumstances,
and interact with method resolution. The relevant rules are designed
specifically to accommodate smart pointers, and so the traits should be used
only for that purpose.
#### Do not fail within a `Deref`/`DerefMut` implementation. [FIXME: needs RFC]
Because the `Deref` traits are invoked implicitly by the compiler in sometimes
subtle ways, failure during dereferencing can be extremely confusing. If a
dereference might not succeed, target the `Deref` trait as a `Result` or
`Option` type instead.
#### Avoid inherent methods when implementing `Deref`/`DerefMut` [FIXME: needs RFC]
The rules around method resolution and `Deref` are in flux, but inherent methods
on a type implementing `Deref` are likely to shadow any methods of the referent
with the same name.

View File

@ -0,0 +1,7 @@
% Using traits to add extension methods
> **[FIXME]** Elaborate.
### Consider using default methods rather than extension traits **[FIXME]**
> **[FIXME]** Elaborate.

View File

@ -0,0 +1,68 @@
% Using traits for bounds on generics
The most widespread use of traits is for writing generic functions or types. For
example, the following signature describes a function for consuming any iterator
yielding items of type `A` to produce a collection of `A`:
```rust
fn from_iter<T: Iterator<A>>(iterator: T) -> SomeCollection<A>
```
Here, the `Iterator` trait is specifies an interface that a type `T` must
explicitly implement to be used by this generic function.
**Pros**:
* _Reusability_. Generic functions can be applied to an open-ended collection of
types, while giving a clear contract for the functionality those types must
provide.
* _Static dispatch and optimization_. Each use of a generic function is
specialized ("monomorphized") to the particular types implementing the trait
bounds, which means that (1) invocations of trait methods are static, direct
calls to the implementation and (2) the compiler can inline and otherwise
optimize these calls.
* _Inline layout_. If a `struct` and `enum` type is generic over some type
parameter `T`, values of type `T` will be laid out _inline_ in the
`struct`/`enum`, without any indirection.
* _Inference_. Since the type parameters to generic functions can usually be
inferred, generic functions can help cut down on verbosity in code where
explicit conversions or other method calls would usually be necessary. See the
[overloading/implicits use case](#use-case:-limited-overloading-and/or-implicit-conversions)
below.
* _Precise types_. Because generic give a _name_ to the specific type
implementing a trait, it is possible to be precise about places where that
exact type is required or produced. For example, a function
```rust
fn binary<T: Trait>(x: T, y: T) -> T
```
is guaranteed to consume and produce elements of exactly the same type `T`; it
cannot be invoked with parameters of different types that both implement
`Trait`.
**Cons**:
* _Code size_. Specializing generic functions means that the function body is
duplicated. The increase in code size must be weighed against the performance
benefits of static dispatch.
* _Homogeneous types_. This is the other side of the "precise types" coin: if
`T` is a type parameter, it stands for a _single_ actual type. So for example
a `Vec<T>` contains elements of a single concrete type (and, indeed, the
vector representation is specialized to lay these out in line). Sometimes
heterogeneous collections are useful; see
[trait objects](#use-case:-trait-objects) below.
* _Signature verbosity_. Heavy use of generics can bloat function signatures.
**[Ed. note]** This problem may be mitigated by some language improvements; stay tuned.
### Favor widespread traits. **[FIXME: needs RFC]**
Generic types are a form of abstraction, which entails a mental indirection: if
a function takes an argument of type `T` bounded by `Trait`, clients must first
think about the concrete types that implement `Trait` to understand how and when
the function is callable.
To keep the cost of abstraction low, favor widely-known traits. Whenever
possible, implement and use traits provided as part of the standard library. Do
not introduce new traits for generics lightly; wait until there are a wide range
of types that can implement the type.

View File

@ -0,0 +1,49 @@
% Using trait objects
> **[FIXME]** What are uses of trait objects other than heterogeneous collections?
Trait objects are useful primarily when _heterogeneous_ collections of objects
need to be treated uniformly; it is the closest that Rust comes to
object-oriented programming.
```rust
struct Frame { ... }
struct Button { ... }
struct Label { ... }
trait Widget { ... }
impl Widget for Frame { ... }
impl Widget for Button { ... }
impl Widget for Label { ... }
impl Frame {
fn new(contents: &[Box<Widget>]) -> Frame {
...
}
}
fn make_gui() -> Box<Widget> {
let b: Box<Widget> = box Button::new(...);
let l: Box<Widget> = box Label::new(...);
box Frame::new([b, l]) as Box<Widget>
}
```
By using trait objects, we can set up a GUI framework with a `Frame` widget that
contains a heterogeneous collection of children widgets.
**Pros**:
* _Heterogeneity_. When you need it, you really need it.
* _Code size_. Unlike generics, trait objects do not generate specialized
(monomorphized) versions of code, which can greatly reduce code size.
**Cons**:
* _No generic methods_. Trait objects cannot currently provide generic methods.
* _Dynamic dispatch and fat pointers_. Trait objects inherently involve
indirection and vtable dispatch, which can carry a performance penalty.
* _No Self_. Except for the method receiver argument, methods on trait objects
cannot use the `Self` type.

View File

@ -0,0 +1,7 @@
% Using traits for overloading
> **[FIXME]** Elaborate.
> **[FIXME]** We need to decide on guidelines for this use case. There are a few
> patterns emerging in current Rust code, but it's not clear how widespread they
> should be.

View File

@ -0,0 +1,30 @@
% Using traits to share implementations
> **[FIXME]** Elaborate.
> **[FIXME]** We probably want to discourage this, at least when used in a way
> that is publicly exposed.
Traits that provide default implmentations for function can provide code reuse
across types. For example, a `print` method can be defined across multiple
types as follows:
``` Rust
trait Printable {
// Default method implementation
fn print(&self) { println!("{:?}", *self) }
}
impl Printable for int {}
impl Printable for String {
fn print(&self) { println!("{}", *self) }
}
impl Printable for bool {}
impl Printable for f32 {}
```
This allows the implementation of `print` to be shared across types, yet
overridden where needed, as seen in the `impl` for `String`.

View File

@ -0,0 +1,68 @@
% Data types
### Use custom types to imbue meaning; do not abuse `bool`, `Option` or other core types. **[FIXME: needs RFC]**
Prefer
```rust
let w = Widget::new(Small, Round)
```
over
```rust
let w = Widget::new(true, false)
```
Core types like `bool`, `u8` and `Option` have many possible interpretations.
Use custom types (whether `enum`s, `struct`, or tuples) to convey
interpretation and invariants. In the above example,
it is not immediately clear what `true` and `false` are conveying without
looking up the argument names, but `Small` and `Round` are more suggestive.
Using custom types makes it easier to expand the
options later on, for example by adding an `ExtraLarge` variant.
See [the newtype pattern](newtype.md) for a no-cost way to wrap
existing types with a distinguished name.
### Prefer private fields, except for passive data. **[FIXME: needs RFC]**
Making a field public is a strong commitment: it pins down a representation
choice, _and_ prevents the type from providing any validation or maintaining any
invariants on the contents of the field, since clients can mutate it arbitrarily.
Public fields are most appropriate for `struct` types in the C spirit: compound,
passive data structures. Otherwise, consider providing getter/setter methods
and hiding fields instead.
> **[FIXME]** Cross-reference validation for function arguments.
### Use custom `enum`s for alternatives, `bitflags` for C-style flags. **[FIXME: needs RFC]**
Rust supports `enum` types with "custom discriminants":
~~~~
enum Color {
Red = 0xff0000,
Green = 0x00ff00,
Blue = 0x0000ff
}
~~~~
Custom discriminants are useful when an `enum` type needs to be serialized to an
integer value compatibly with some other system/language. They support
"typesafe" APIs: by taking a `Color`, rather than an integer, a function is
guaranteed to get well-formed inputs, even if it later views those inputs as
integers.
An `enum` allows an API to request exactly one choice from among many. Sometimes
an API's input is instead the presence or absence of a set of flags. In C code,
this is often done by having each flag correspond to a particular bit, allowing
a single integer to represent, say, 32 or 64 flags. Rust's `std::bitflags`
module provides a typesafe way for doing so.
### Phantom types. [FIXME]
> **[FIXME]** Add some material on phantom types (https://blog.mozilla.org/research/2014/06/23/static-checking-of-units-in-servo/)

View File

@ -0,0 +1,22 @@
% Conversions between types
### Associate conversions with the most specific type involved. **[FIXME: needs RFC]**
When in doubt, prefer `to_`/`as_`/`into_` to `from_`, because they are
more ergonomic to use (and can be chained with other methods).
For many conversions between two types, one of the types is clearly more
"specific": it provides some additional invariant or interpretation that is not
present in the other type. For example, `str` is more specific than `&[u8]`,
since it is a utf-8 encoded sequence of bytes.
Conversions should live with the more specific of the involved types. Thus,
`str` provides both the `as_bytes` method and the `from_utf8` constructor for
converting to and from `&[u8]` values. Besides being intuitive, this convention
avoids polluting concrete types like `&[u8]` with endless conversion methods.
### Explicitly mark lossy conversions, or do not label them as conversions. **[FIXME: needs RFC]**
If a function's name implies that it is a conversion (prefix `from_`, `as_`,
`to_` or `into_`), but the function loses information, add a suffix `_lossy` or
otherwise indicate the lossyness. Consider avoiding the conversion name prefix.

View File

@ -0,0 +1,69 @@
% The newtype pattern
A "newtype" is a tuple or `struct` with a single field. The terminology is borrowed from Haskell.
Newtypes are a zero-cost abstraction: they introduce a new, distinct name for an
existing type, with no runtime overhead when converting between the two types.
### Use newtypes to provide static distinctions. [FIXME: needs RFC]
Newtypes can statically distinguish between different interpretations of an
underlying type.
For example, a `f64` value might be used to represent a quantity in miles or in
kilometers. Using newtypes, we can keep track of the intended interpretation:
```rust
struct Miles(pub f64);
struct Kilometers(pub f64);
impl Miles {
fn as_kilometers(&self) -> Kilometers { ... }
}
impl Kilometers {
fn as_miles(&self) -> Miles { ... }
}
```
Once we have separated these two types, we can statically ensure that we do not
confuse them. For example, the function
```rust
fn are_we_there_yet(distance_travelled: Miles) -> bool { ... }
```
cannot accidentally be called with a `Kilometers` value. The compiler will
remind us to perform the conversion, thus averting certain
[catastrophic bugs](http://en.wikipedia.org/wiki/Mars_Climate_Orbiter).
### Use newtypes with private fields for hiding. [FIXME: needs RFC]
A newtype can be used to hide representation details while making precise
promises to the client.
For example, consider a function `my_transform` that returns a compound iterator
type `Enumerate<Skip<vec::MoveItems<T>>>`. We wish to hide this type from the
client, so that the client's view of the return type is roughly `Iterator<(uint,
T)>`. We can do so using the newtype pattern:
```rust
struct MyTransformResult<T>(Enumerate<Skip<vec::MoveItems<T>>>);
impl<T> Iterator<(uint, T)> for MyTransformResult<T> { ... }
fn my_transform<T, Iter: Iterator<T>>(iter: Iter) -> MyTransformResult<T> {
...
}
```
Aside from simplifying the signature, this use of newtypes allows us to make a
expose and promise less to the client. The client does not know _how_ the result
iterator is constructed or represented, which means the representation can
change in the future without breaking client code.
> **[FIXME]** Interaction with auto-deref.
### Use newtypes to provide cost-free _views_ of another type. **[FIXME]**
> **[FIXME]** Describe the pattern of using newtypes to provide a new set of
> inherent or trait methods, providing a different perspective on the underlying
> type.

View File

@ -0,0 +1,3 @@
% Ownership and resource management
> **[FIXME]** Add general remarks about ownership/resources here.

View File

@ -0,0 +1,176 @@
% The builder pattern
Some data structures are complicated to construct, due to their construction needing:
* a large number of inputs
* compound data (e.g. slices)
* optional configuration data
* choice between several flavors
which can easily lead to a large number of distinct constructors with
many arguments each.
If `T` is such a data structure, consider introducing a `T` _builder_:
1. Introduce a separate data type `TBuilder` for incrementally configuring a `T`
value. When possible, choose a better name: e.g. `Command` is the builder for
`Process`.
2. The builder constructor should take as parameters only the data _required_ to
to make a `T`.
3. The builder should offer a suite of convenient methods for configuration,
including setting up compound inputs (like slices) incrementally.
These methods should return `self` to allow chaining.
4. The builder should provide one or more "_terminal_" methods for actually building a `T`.
The builder pattern is especially appropriate when building a `T` involves side
effects, such as spawning a task or launching a process.
In Rust, there are two variants of the builder pattern, differing in the
treatment of ownership, as described below.
### Non-consuming builders (preferred):
In some cases, constructing the final `T` does not require the builder itself to
be consumed. The follow variant on
[`std::io::process::Command`](http://static.rust-lang.org/doc/master/std/io/process/struct.Command.html)
is one example:
```rust
// NOTE: the actual Command API does not use owned Strings;
// this is a simplified version.
pub struct Command {
program: String,
args: Vec<String>,
cwd: Option<String>,
// etc
}
impl Command {
pub fn new(program: String) -> Command {
Command {
program: program,
args: Vec::new(),
cwd: None,
}
}
/// Add an argument to pass to the program.
pub fn arg<'a>(&'a mut self, arg: String) -> &'a mut Command {
self.args.push(arg);
self
}
/// Add multiple arguments to pass to the program.
pub fn args<'a>(&'a mut self, args: &[String])
-> &'a mut Command {
self.args.push_all(args);
self
}
/// Set the working directory for the child process.
pub fn cwd<'a>(&'a mut self, dir: String) -> &'a mut Command {
self.cwd = Some(dir);
self
}
/// Executes the command as a child process, which is returned.
pub fn spawn(&self) -> IoResult<Process> {
...
}
}
```
Note that the `spawn` method, which actually uses the builder configuration to
spawn a process, takes the builder by immutable reference. This is possible
because spawning the process does not require ownership of the configuration
data.
Because the terminal `spawn` method only needs a reference, the configuration
methods take and return a mutable borrow of `self`.
#### The benefit
By using borrows throughout, `Command` can be used conveniently for both
one-liner and more complex constructions:
```rust
// One-liners
Command::new("/bin/cat").arg("file.txt").spawn();
// Complex configuration
let mut cmd = Command::new("/bin/ls");
cmd.arg(".");
if size_sorted {
cmd.arg("-S");
}
cmd.spawn();
```
### Consuming builders:
Sometimes builders must transfer ownership when constructing the final type
`T`, meaning that the terminal methods must take `self` rather than `&self`:
```rust
// A simplified excerpt from std::task::TaskBuilder
impl TaskBuilder {
/// Name the task-to-be. Currently the name is used for identification
/// only in failure messages.
pub fn named(mut self, name: String) -> TaskBuilder {
self.name = Some(name);
self
}
/// Redirect task-local stdout.
pub fn stdout(mut self, stdout: Box<Writer + Send>) -> TaskBuilder {
self.stdout = Some(stdout);
// ^~~~~~ this is owned and cannot be cloned/re-used
self
}
/// Creates and executes a new child task.
pub fn spawn(self, f: proc():Send) {
// consume self
...
}
}
```
Here, the `stdout` configuration involves passing ownership of a `Writer`,
which must be transferred to the task upon construction (in `spawn`).
When the terminal methods of the builder require ownership, there is a basic tradeoff:
* If the other builder methods take/return a mutable borrow, the complex
configuration case will work well, but one-liner configuration becomes
_impossible_.
* If the other builder methods take/return an owned `self`, one-liners
continue to work well but complex configuration is less convenient.
Under the rubric of making easy things easy and hard things possible, _all_
builder methods for a consuming builder should take and returned an owned
`self`. Then client code works as follows:
```rust
// One-liners
TaskBuilder::new().named("my_task").spawn(proc() { ... });
// Complex configuration
let mut task = TaskBuilder::new();
task = task.named("my_task_2"); // must re-assign to retain ownership
if reroute {
task = task.stdout(mywriter);
}
task.spawn(proc() { ... });
```
One-liners work as before, because ownership is threaded through each of the
builder methods until being consumed by `spawn`. Complex configuration,
however, is more verbose: it requires re-assigning the builder at each step.

View File

@ -0,0 +1,4 @@
% Cells and smart pointers
> **[FIXME]** Add guidelines about when to use Cell, RefCell, Rc and
> Arc (and how to use them together).

View File

@ -0,0 +1,62 @@
% Constructors
### Define constructors as static, inherent methods. [FIXME: needs RFC]
In Rust, "constructors" are just a convention:
```rust
impl<T> Vec<T> {
pub fn new() -> Vec<T> { ... }
}
```
Constructors are static (no `self`) inherent methods for the type that they
construct. Combined with the practice of
[fully importing type names](../style/imports.md), this convention leads to
informative but concise construction:
```rust
use vec::Vec;
// construct a new vector
let mut v = Vec::new();
```
This convention also applied to conversion constructors (prefix `from` rather
than `new`).
### Provide constructors for passive `struct`s with defaults. [FIXME: needs RFC]
Given the `struct`
```rust
pub struct Config {
pub color: Color,
pub size: Size,
pub shape: Shape,
}
```
provide a constructor if there are sensible defaults:
```rust
impl Config {
pub fn new() -> Config {
Config {
color: Brown,
size: Medium,
shape: Square,
}
}
}
```
which then allows clients to concisely override using `struct` update syntax:
```rust
Config { color: Red, .. Config::new() };
```
See the [guideline for field privacy](../features/types/README.md) for
discussion on when to create such "passive" `struct`s with public
fields.

View File

@ -0,0 +1,22 @@
% Destructors
Unlike constructors, destructors in Rust have a special status: they are added
by implementing `Drop` for a type, and they are automatically invoked as values
go out of scope.
> **[FIXME]** This section needs to be expanded.
### Destructors should not fail. [FIXME: needs RFC]
Destructors are executed on task failure, and in that context a failing
destructor causes the program to abort.
Instead of failing in a destructor, provide a separate method for checking for
clean teardown, e.g. a `close` method, that returns a `Result` to signal
problems.
### Destructors should not block. [FIXME: needs RFC]
Similarly, destructors should not invoke blocking operations, which can make
debugging much more difficult. Again, consider providing a separate method for
preparing for an infallible, nonblocking teardown.

View File

@ -0,0 +1,12 @@
% RAII
Resource Acquisition is Initialization
> **[FIXME]** Explain the RAII pattern and give best practices.
### Whenever possible, tie resource access to guard scopes [FIXME]
> **[FIXME]** Example: Mutex guards guarantee that access to the
> protected resource only happens when the guard is in scope.
`must_use`

View File

@ -0,0 +1,7 @@
% FFI and platform-specific code **[FIXME]**
> **[FIXME]** Not sure where this should live.
When writing cross-platform code, group platform-specific code into a
module called `platform`. Avoid `#[cfg]` directives outside this
`platform` module.

View File

@ -0,0 +1,19 @@
% Safety and guarantees
> **[FIXME]** Is there a better phrase than "strong guarantees" that encompasses
> both e.g. memory safety and e.g. data structure invariants?
A _guarantee_ is a property that holds no matter what client code does, unless
the client explicitly opts out:
* Rust guarantees memory safety and data-race freedom, with `unsafe`
blocks as an opt-out mechanism.
* APIs in Rust often provide their own guarantees. For example, `std::str`
guarantees that its underlying buffer is valid utf-8. The `std::path::Path` type
guarantees no interior nulls. Both strings and paths provide `unsafe` mechanisms
for opting out of these guarantees (and thereby avoiding runtime checks).
Thinking about guarantees is an essential part of writing good Rust code. The
rest of this subsection outlines some cross-cutting principles around
guarantees.

View File

@ -0,0 +1,81 @@
% Library-level guarantees
Most libraries rely on internal invariants, e.g. about their data, resource
ownership, or protocol states. In Rust, broken invariants cannot produce
segfaults, but they can still lead to wrong answers.
### Provide library-level guarantees whenever practical. **[FIXME: needs RFC]**
Library-level invariants should be turned into guarantees whenever
practical. They should hold no matter what the client does, modulo
explicit opt-outs. Depending on the kind of invariant, this can be
achieved through a combination of static and dynamic enforcement, as
described below.
#### Static enforcement:
Guaranteeing invariants almost always requires _hiding_,
i.e. preventing the client from directly accessing or modifying
internal data.
For example, the representation of the `str` type is hidden,
which means that any value of type `str` must have been produced
through an API under the control of the `str` module, and these
APIs in turn ensure valid utf-8 encoding.
Rust's type system makes it possible to provide guarantees even while
revealing more of the representation than usual. For example, the
`as_bytes()` method on `&str` gives a _read-only_ view into the
underlying buffer, which cannot be used to violate the utf-8 property.
#### Dynamic enforcement:
Malformed inputs from the client are hazards to library-level
guarantees, so library APIs should validate their input.
For example, `std::str::from_utf8_owned` attempts to convert a `u8`
slice into an owned string, but dynamically checks that the slice is
valid utf-8 and returns `Err` if not.
See
[the discussion on input validation](../features/functions-and-methods/input.md)
for more detail.
### Prefer static enforcement of guarantees. **[FIXME: needs RFC]**
Static enforcement provides two strong benefits over dynamic enforcement:
* Bugs are caught at compile time.
* There is no runtime cost.
Sometimes purely static enforcement is impossible or impractical. In these
cases, a library should check as much as possible statically, but defer to
dynamic checks where needed.
For example, the `std::string` module exports a `String` type with the guarantee
that all instances are valid utf-8:
* Any _consumer_ of a `String` is statically guaranteed utf-8 contents. For example,
the `append` method can push a `&str` onto the end of a `String` without
checking anything dynamically, since the existing `String` and `&str` are
statically guaranteed to be in utf-8.
* Some _producers_ of a `String` must perform dynamic checks. For example, the
`from_utf8` function attempts to convert a `Vec<u8>` into a `String`, but
dynamically checks that the contents are utf-8.
### Provide opt-outs with caution; make them explicit. **[FIXME: needs RFC]**
Providing library-level guarantees sometimes entails inconvenience (for static
checks) or overhead (for dynamic checks). So it is sometimes desirable to allow
clients to sidestep this checking, while promising to use the API in a way that
still provides the guarantee. Such escape hatches should only be be introduced
when there is a demonstrated need for them.
It should be trivial for clients to audit their use of the library for
escape hatches.
See
[the discussion on input validation](../features/functions-and-methods/input.md)
for conventions on marking opt-out functions.

View File

@ -0,0 +1,22 @@
% Using `unsafe`
### Unconditionally guarantee safety, or mark API as `unsafe`. **[FIXME: needs RFC]**
Memory safety, type safety, and data race freedom are basic assumptions for all
Rust code.
APIs that use `unsafe` blocks internally thus have two choices:
* They can guarantee safety _unconditionally_ (i.e., regardless of client
behavior or inputs) and be exported as safe code. Any safety violation is then
the library's fault, not the client's fault.
* They can export potentially unsafe functions with the `unsafe` qualifier. In
this case, the documentation should make very clear the conditions under which
safety is guaranteed.
The result is that a client program can never violate safety merely by having a
bug; it must have explicitly opted out by using an `unsafe` block.
Of the two options for using `unsafe`, creating such safe abstractions (the
first option above) is strongly preferred.

View File

@ -0,0 +1,5 @@
% Style
This section gives a set of strict rules for styling Rust code.
> **[FIXME]** General remarks about the style guidelines

View File

@ -0,0 +1,77 @@
% Braces, semicolons, and commas [FIXME: needs RFC]
### Opening braces always go on the same line.
``` rust
fn foo() {
...
}
fn frobnicate(a: Bar, b: Bar,
c: Bar, d: Bar)
-> Bar {
...
}
trait Bar {
fn baz(&self);
}
impl Bar for Baz {
fn baz(&self) {
...
}
}
frob(|x| {
x.transpose()
})
```
### `match` arms get braces, except for single-line expressions.
``` rust
match foo {
bar => baz,
quux => {
do_something();
do_something_else()
}
}
```
### `return` statements get semicolons.
``` rust
fn foo() {
do_something();
if condition() {
return;
}
do_something_else();
}
```
### Trailing commas
> **[FIXME]** We should have a guideline for when to include trailing
> commas in `struct`s, `match`es, function calls, etc.
>
> One possible rule: a trailing comma should be included whenever the
> closing delimiter appears on a separate line:
```rust
Foo { bar: 0, baz: 1 }
Foo {
bar: 0,
baz: 1,
}
match a_thing {
None => 0,
Some(x) => 1,
}
```

View File

@ -0,0 +1,87 @@
% Comments [FIXME: needs RFC]
### Avoid block comments.
Use line comments:
``` rust
// Wait for the main task to return, and set the process error code
// appropriately.
```
Instead of:
``` rust
/*
* Wait for the main task to return, and set the process error code
* appropriately.
*/
```
## Doc comments
Doc comments are prefixed by three slashes (`///`) and indicate
documentation that you would like to be included in Rustdoc's output.
They support
[Markdown syntax](https://en.wikipedia.org/wiki/Markdown)
and are the main way of documenting your public APIs.
The supported markdown syntax includes all of the extensions listed in the
[GitHub Flavored Markdown]
(https://help.github.com/articles/github-flavored-markdown) documentation,
plus superscripts.
### Summary line
The first line in any doc comment should be a single-line short sentence
providing a summary of the code. This line is used as a short summary
description throughout Rustdoc's output, so it's a good idea to keep it
short.
### Sentence structure
All doc comments, including the summary line, should begin with a
capital letter and end with a period, question mark, or exclamation
point. Prefer full sentences to fragments.
The summary line should be written in
[third person singular present indicative form]
(http://en.wikipedia.org/wiki/English_verbs#Third_person_singular_present).
Basically, this means write "Returns" instead of "Return".
For example:
``` rust
/// Sets up a default runtime configuration, given compiler-supplied arguments.
///
/// This function will block until the entire pool of M:N schedulers has
/// exited. This function also requires a local task to be available.
///
/// # Arguments
///
/// * `argc` & `argv` - The argument vector. On Unix this information is used
/// by `os::args`.
/// * `main` - The initial procedure to run inside of the M:N scheduling pool.
/// Once this procedure exits, the scheduling pool will begin to shut
/// down. The entire pool (and this function) will only return once
/// all child tasks have finished executing.
///
/// # Return value
///
/// The return value is used as the process return code. 0 on success, 101 on
/// error.
```
### Code snippets
> **[FIXME]**
### Avoid inner doc comments.
Use inner doc comments _only_ to document crates and file-level modules:
``` rust
//! The core library.
//!
//! The core library is a something something...
```

View File

@ -0,0 +1,13 @@
## `return` [FIXME: needs RFC]
Terminate `return` statements with semicolons:
``` rust
fn foo(bar: int) -> Option<int> {
if some_condition() {
return None;
}
...
}
```

View File

@ -0,0 +1,50 @@
% Imports [FIXME: needs RFC]
The imports of a crate/module should consist of the following
sections, in order, with a blank space between each:
* `extern crate` directives
* external `use` imports
* local `use` imports
* `pub use` imports
For example:
```rust
// Crates.
extern crate getopts;
extern crate mylib;
// Standard library imports.
use getopts::{optopt, getopts};
use std::os;
// Import from a library that we wrote.
use mylib::webserver;
// Will be reexported when we import this module.
pub use self::types::Webdata;
```
### Avoid `use *`, except in tests.
Glob imports have several downsides:
* They make it harder to tell where names are bound.
* They are forwards-incompatible, since new upstream exports can clash
with existing names.
When writing a [`test` submodule](../testing/README.md), importing `super::*` is appropriate
as a convenience.
### Prefer fully importing types/traits while module-qualifying functions.
For example:
```rust
use option::Option;
use mem;
let i: int = mem::transmute(Option(0));
```
> **[FIXME]** Add rationale.

View File

@ -0,0 +1,115 @@
% Naming conventions
### General conventions [RFC #430]
> The guidelines below were approved by [RFC #430](https://github.com/rust-lang/rfcs/pull/430).
In general, Rust tends to use `CamelCase` for "type-level" constructs
(types and traits) and `snake_case` for "value-level" constructs. More
precisely:
| Item | Convention |
| ---- | ---------- |
| Crates | `snake_case` (but prefer single word) |
| Modules | `snake_case` |
| Types | `CamelCase` |
| Traits | `CamelCase` |
| Enum variants | `CamelCase` |
| Functions | `snake_case` |
| Methods | `snake_case` |
| General constructors | `new` or `with_more_details` |
| Conversion constructors | `from_some_other_type` |
| Local variables | `snake_case` |
| Static variables | `SCREAMING_SNAKE_CASE` |
| Constant variables | `SCREAMING_SNAKE_CASE` |
| Type parameters | concise `CamelCase`, usually single uppercase letter: `T` |
| Lifetimes | short, lowercase: `'a` |
<p>
In `CamelCase`, acronyms count as one word: use `Uuid` rather than
`UUID`. In `snake_case`, acronyms are lower-cased: `is_xid_start`.
In `snake_case` or `SCREAMING_SNAKE_CASE`, a "word" should never
consist of a single letter unless it is the last "word". So, we have
`btree_map` rather than `b_tree_map`, but `PI_2` rather than `PI2`.
### Referring to types in function/method names [RFC 344]
> The guidelines below were approved by [RFC #344](https://github.com/rust-lang/rfcs/pull/344).
Function names often involve type names, the most common example being conversions
like `as_slice`. If the type has a purely textual name (ignoring parameters), it
is straightforward to convert between type conventions and function conventions:
Type name | Text in methods
--------- | ---------------
`String` | `string`
`Vec<T>` | `vec`
`YourType`| `your_type`
Types that involve notation follow the convention below. There is some
overlap on these rules; apply the most specific applicable rule:
Type name | Text in methods
--------- | ---------------
`&str` | `str`
`&[T]` | `slice`
`&mut [T]`| `mut_slice`
`&[u8]` | `bytes`
`&T` | `ref`
`&mut T` | `mut`
`*const T`| `ptr`
`*mut T` | `mut_ptr`
### Avoid redundant prefixes [RFC 356]
> The guidelines below were approved by [RFC #356](https://github.com/rust-lang/rfcs/pull/356).
Names of items within a module should not be prefixed with that module's name:
Prefer
``` rust
mod foo {
pub struct Error { ... }
}
```
over
``` rust
mod foo {
pub struct FooError { ... }
}
```
This convention avoids stuttering (like `io::IoError`). Library clients can
rename on import to avoid clashes.
### Getter/setter methods [RFC 344]
> The guidelines below were approved by [RFC #344](https://github.com/rust-lang/rfcs/pull/344).
Some data structures do not wish to provide direct access to their fields, but
instead offer "getter" and "setter" methods for manipulating the field state
(often providing checking or other functionality).
The convention for a field `foo: T` is:
* A method `foo(&self) -> &T` for getting the current value of the field.
* A method `set_foo(&self, val: T)` for setting the field. (The `val` argument
here may take `&T` or some other type, depending on the context.)
Note that this convention is about getters/setters on ordinary data types, *not*
on [builder objects](../ownership/builders.html).
### Escape hatches [FIXME]
> **[FIXME]** Should we standardize a convention for functions that may break API
> guarantees? e.g. `ToCStr::to_c_str_unchecked`
### Predicates
* Simple boolean predicates should be prefixed with `is_` or another
short question word, e.g., `is_empty`.
* Common exceptions: `lt`, `gt`, and other established predicate names.

View File

@ -0,0 +1,69 @@
% Common container/wrapper methods [FIXME: needs RFC]
Containers, wrappers, and cells all provide ways to access the data
they enclose. Accessor methods often have variants to access the data
by value, by reference, and by mutable reference.
In general, the `get` family of methods is used to access contained
data without any risk of task failure; they return `Option` as
appropriate. This name is chosen rather than names like `find` or
`lookup` because it is appropriate for a wider range of container types.
#### Containers
For a container with keys/indexes of type `K` and elements of type `V`:
```rust
// Look up element without failing
fn get(&self, key: K) -> Option<&V>
fn get_mut(&mut self, key: K) -> Option<&mut V>
// Convenience for .get(key).map(|elt| elt.clone())
fn get_clone(&self, key: K) -> Option<V>
// Lookup element, failing if it is not found:
impl Index<K, V> for Container { ... }
impl IndexMut<K, V> for Container { ... }
```
#### Wrappers/Cells
Prefer specific conversion functions like `as_bytes` or `into_vec` whenever
possible. Otherwise, use:
```rust
// Extract contents without failing
fn get(&self) -> &V
fn get_mut(&mut self) -> &mut V
fn unwrap(self) -> V
```
#### Wrappers/Cells around `Copy` data
```rust
// Extract contents without failing
fn get(&self) -> V
```
#### `Option`-like types
Finally, we have the cases of types like `Option` and `Result`, which
play a special role for failure.
For `Option<V>`:
```rust
// Extract contents or fail if not available
fn assert(self) -> V
fn expect(self, &str) -> V
```
For `Result<V, E>`:
```rust
// Extract the contents of Ok variant; fail if Err
fn assert(self) -> V
// Extract the contents of Err variant; fail if Ok
fn assert_err(self) -> E
```

View File

@ -0,0 +1,32 @@
% Conversions [Rust issue #7087]
> The guidelines below were approved by [rust issue #7087](https://github.com/rust-lang/rust/issues/7087).
> **[FIXME]** Should we provide standard traits for conversions? Doing
> so nicely will require
> [trait reform](https://github.com/rust-lang/rfcs/pull/48) to land.
Conversions should be provided as methods, with names prefixed as follows:
| Prefix | Cost | Consumes convertee |
| ------ | ---- | ------------------ |
| `as_` | Free | No |
| `to_` | Expensive | No |
| `into_` | Variable | Yes |
<p>
For example:
* `as_bytes()` gives a `&[u8]` view into a `&str`, which is a no-op.
* `to_owned()` copies a `&str` to a new `String`.
* `into_bytes()` consumes a `String` and yields the underlying
`Vec<u8>`, which is a no-op.
Conversions prefixed `as_` and `into_` typically _decrease abstraction_, either
exposing a view into the underlying representation (`as`) or deconstructing data
into its underlying representation (`into`). Conversions prefixed `to_`, on the
other hand, typically stay at the same level of abstraction but do some work to
change one representation into another.
> **[FIXME]** The distinctions between conversion methods does not work
> so well for `from_` conversion constructors. Is that a problem?

View File

@ -0,0 +1,32 @@
% Iterators
#### Method names [RFC #199]
> The guidelines below were approved by [RFC #199](https://github.com/rust-lang/rfcs/pull/199).
For a container with elements of type `U`, iterator methods should be named:
```rust
fn iter(&self) -> T // where T implements Iterator<&U>
fn iter_mut(&mut self) -> T // where T implements Iterator<&mut U>
fn into_iter(self) -> T // where T implements Iterator<U>
```
The default iterator variant yields shared references `&U`.
#### Type names [RFC #344]
> The guidelines below were approved by [RFC #344](https://github.com/rust-lang/rfcs/pull/344).
The name of an iterator type should be the same as the method that
produces the iterator.
For example:
* `iter` should yield an `Iter`
* `iter_mut` should yield an `IterMut`
* `into_iter` should yield an `IntoIter`
* `keys` should yield `Keys`
These type names make the most sense when prefixed with their owning module,
e.g. `vec::IntoIter`.

View File

@ -0,0 +1,34 @@
% Ownership variants [RFC #199]
> The guidelines below were approved by [RFC #199](https://github.com/rust-lang/rfcs/pull/199).
Functions often come in multiple variants: immutably borrowed, mutably
borrowed, and owned.
The right default depends on the function in question. Variants should
be marked through suffixes.
#### Immutably borrowed by default
If `foo` uses/produces an immutable borrow by default, use:
* The `_mut` suffix (e.g. `foo_mut`) for the mutably borrowed variant.
* The `_move` suffix (e.g. `foo_move`) for the owned variant.
#### Owned by default
If `foo` uses/produces owned data by default, use:
* The `_ref` suffix (e.g. `foo_ref`) for the immutably borrowed variant.
* The `_mut` suffix (e.g. `foo_mut`) for the mutably borrowed variant.
#### Exceptions
In the case of iterators, the moving variant can also be understood as
an `into` conversion, `into_iter`, and `for x in v.into_iter()` reads
arguably better than `for x in v.iter_move()`, so the convention is
`into_iter`.
For mutably borrowed variants, if the `mut` qualifier is part of a
type name (e.g. `as_mut_slice`), it should appear as it would appear
in the type.

View File

@ -0,0 +1,3 @@
*
*

View File

@ -0,0 +1,14 @@
% Organization [FIXME: needs RFC]
> **[FIXME]** What else?
### Reexport the most important types at the crate level.
Crates `pub use` the most common types for convenience, so that clients do not
have to remember or write the crate's module hierarchy to use these types.
### Define types and operations together.
Type definitions and the functions/methods that operate on them should be
defined together in a single module, with the type appearing above the
functions/methods.

View File

@ -0,0 +1,133 @@
% Whitespace [FIXME: needs RFC]
* Lines must not exceed 99 characters.
* Use 4 spaces for indentation, _not_ tabs.
* No trailing whitespace at the end of lines or files.
### Spaces
* Use spaces around binary operators, including the equals sign in attributes:
``` rust
#[deprecated = "Use `bar` instead."]
fn foo(a: uint, b: uint) -> uint {
a + b
}
```
* Use a space after colons and commas:
``` rust
fn foo(a: Bar);
MyStruct { foo: 3, bar: 4 }
foo(bar, baz);
```
* Use a space after the opening and before the closing brace for
single line blocks or `struct` expressions:
``` rust
spawn(proc() { do_something(); })
Point { x: 0.1, y: 0.3 }
```
### Line wrapping
* For multiline function signatures, each new line should align with the
first parameter. Multiple parameters per line are permitted:
``` rust
fn frobnicate(a: Bar, b: Bar,
c: Bar, d: Bar)
-> Bar {
...
}
fn foo<T: This,
U: That>(
a: Bar,
b: Bar)
-> Baz {
...
}
```
* Multiline function invocations generally follow the same rule as for
signatures. However, if the final argument begins a new block, the
contents of the block may begin on a new line, indented one level:
``` rust
fn foo_bar(a: Bar, b: Bar,
c: |Bar|) -> Bar {
...
}
// Same line is fine:
foo_bar(x, y, |z| { z.transpose(y) });
// Indented body on new line is also fine:
foo_bar(x, y, |z| {
z.quux();
z.rotate(x)
})
```
> **[FIXME]** Do we also want to allow the following?
>
> ```rust
> frobnicate(
> arg1,
> arg2,
> arg3)
> ```
>
> This style could ease the conflict between line length and functions
> with many parameters (or long method chains).
### Matches
> * **[Deprecated]** If you have multiple patterns in a single `match`
> arm, write each pattern on a separate line:
>
> ``` rust
> match foo {
> bar(_)
> | baz => quux,
> x
> | y
> | z => {
> quuux
> }
> }
> ```
### Alignment
Idiomatic code should not use extra whitespace in the middle of a line
to provide alignment.
``` rust
// Good
struct Foo {
short: f64,
really_long: f64,
}
// Bad
struct Bar {
short: f64,
really_long: f64,
}
// Good
let a = 0;
let radius = 7;
// Bad
let b = 0;
let diameter = 7;
```

View File

@ -0,0 +1,5 @@
% Testing
> **[FIXME]** Add some general remarks about when and how to unit
> test, versus other kinds of testing. What are our expectations for
> Rust's core libraries?

View File

@ -0,0 +1,30 @@
% Unit testing
Unit tests should live in a `test` submodule at the bottom of the module they
test. Mark the `test` submodule with `#[cfg(test)]` so it is only compiled when
testing.
The `test` module should contain:
* Imports needed only for testing.
* Functions marked with `#[test]` striving for full coverage of the parent module's
definitions.
* Auxiliary functions needed for writing the tests.
For example:
``` rust
// Excerpt from std::str
#[cfg(test)]
mod test {
#[test]
fn test_eq() {
assert!((eq(&"".to_owned(), &"".to_owned())));
assert!((eq(&"foo".to_owned(), &"foo".to_owned())));
assert!((!eq(&"foo".to_owned(), &"bar".to_owned())));
}
}
```
> **[FIXME]** add details about useful macros for testing, e.g. `assert!`

5
src/doc/style/todo.md Normal file
View File

@ -0,0 +1,5 @@
* [Containers and iteration]()
* [The visitor pattern]()
* [Concurrency]()
* [Documentation]()
* [Macros]()