rollup merge of #22331: steveklabnik/guidelines
Fixes #19315 r? @aturon
This commit is contained in:
commit
25ccf3c0da
58 changed files with 2554 additions and 1 deletions
|
@ -73,7 +73,7 @@ RUSTBOOK = $(RPATH_VAR2_T_$(CFG_BUILD)_H_$(CFG_BUILD)) $(RUSTBOOK_EXE)
|
||||||
|
|
||||||
D := $(S)src/doc
|
D := $(S)src/doc
|
||||||
|
|
||||||
DOC_TARGETS := trpl
|
DOC_TARGETS := trpl style
|
||||||
COMPILER_DOC_TARGETS :=
|
COMPILER_DOC_TARGETS :=
|
||||||
DOC_L10N_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/
|
doc/book/index.html: $(RUSTBOOK_EXE) $(wildcard $(S)/src/doc/trpl/*.md) | doc/
|
||||||
$(Q)rm -rf doc/book
|
$(Q)rm -rf doc/book
|
||||||
$(Q)$(RUSTBOOK) build $(S)src/doc/trpl 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
64
src/doc/style/README.md
Normal 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
54
src/doc/style/SUMMARY.md
Normal 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)
|
5
src/doc/style/changing/README.md
Normal file
5
src/doc/style/changing/README.md
Normal 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.
|
12
src/doc/style/changing/post-1-0.md
Normal file
12
src/doc/style/changing/post-1-0.md
Normal 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.
|
17
src/doc/style/changing/pre-1-0.md
Normal file
17
src/doc/style/changing/pre-1-0.md
Normal 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
|
28
src/doc/style/changing/unclear.md
Normal file
28
src/doc/style/changing/unclear.md
Normal 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
|
3
src/doc/style/errors/README.md
Normal file
3
src/doc/style/errors/README.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
% Errors
|
||||||
|
|
||||||
|
> **[FIXME]** Add some general text here.
|
66
src/doc/style/errors/ergonomics.md
Normal file
66
src/doc/style/errors/ergonomics.md
Normal 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.
|
7
src/doc/style/errors/handling.md
Normal file
7
src/doc/style/errors/handling.md
Normal 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]
|
8
src/doc/style/errors/propagation.md
Normal file
8
src/doc/style/errors/propagation.md
Normal 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.
|
125
src/doc/style/errors/signaling.md
Normal file
125
src/doc/style/errors/signaling.md
Normal 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.
|
9
src/doc/style/features/README.md
Normal file
9
src/doc/style/features/README.md
Normal 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).
|
6
src/doc/style/features/crates.md
Normal file
6
src/doc/style/features/crates.md
Normal 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"
|
43
src/doc/style/features/functions-and-methods/README.md
Normal file
43
src/doc/style/features/functions-and-methods/README.md
Normal 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.
|
43
src/doc/style/features/functions-and-methods/convenience.md
Normal file
43
src/doc/style/features/functions-and-methods/convenience.md
Normal 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.
|
201
src/doc/style/features/functions-and-methods/input.md
Normal file
201
src/doc/style/features/functions-and-methods/input.md
Normal 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`?
|
56
src/doc/style/features/functions-and-methods/output.md
Normal file
56
src/doc/style/features/functions-and-methods/output.md
Normal 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.
|
103
src/doc/style/features/let.md
Normal file
103
src/doc/style/features/let.md
Normal 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.
|
13
src/doc/style/features/loops.md
Normal file
13
src/doc/style/features/loops.md
Normal 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`
|
26
src/doc/style/features/match.md
Normal file
26
src/doc/style/features/match.md
Normal 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` -->
|
133
src/doc/style/features/modules.md
Normal file
133
src/doc/style/features/modules.md
Normal 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.
|
22
src/doc/style/features/traits/README.md
Normal file
22
src/doc/style/features/traits/README.md
Normal 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.
|
71
src/doc/style/features/traits/common.md
Normal file
71
src/doc/style/features/traits/common.md
Normal 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.
|
7
src/doc/style/features/traits/extensions.md
Normal file
7
src/doc/style/features/traits/extensions.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
% Using traits to add extension methods
|
||||||
|
|
||||||
|
> **[FIXME]** Elaborate.
|
||||||
|
|
||||||
|
### Consider using default methods rather than extension traits **[FIXME]**
|
||||||
|
|
||||||
|
> **[FIXME]** Elaborate.
|
68
src/doc/style/features/traits/generics.md
Normal file
68
src/doc/style/features/traits/generics.md
Normal 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.
|
49
src/doc/style/features/traits/objects.md
Normal file
49
src/doc/style/features/traits/objects.md
Normal 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.
|
7
src/doc/style/features/traits/overloading.md
Normal file
7
src/doc/style/features/traits/overloading.md
Normal 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.
|
30
src/doc/style/features/traits/reuse.md
Normal file
30
src/doc/style/features/traits/reuse.md
Normal 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`.
|
68
src/doc/style/features/types/README.md
Normal file
68
src/doc/style/features/types/README.md
Normal 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/)
|
22
src/doc/style/features/types/conversions.md
Normal file
22
src/doc/style/features/types/conversions.md
Normal 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.
|
69
src/doc/style/features/types/newtype.md
Normal file
69
src/doc/style/features/types/newtype.md
Normal 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.
|
3
src/doc/style/ownership/README.md
Normal file
3
src/doc/style/ownership/README.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
% Ownership and resource management
|
||||||
|
|
||||||
|
> **[FIXME]** Add general remarks about ownership/resources here.
|
176
src/doc/style/ownership/builders.md
Normal file
176
src/doc/style/ownership/builders.md
Normal 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.
|
4
src/doc/style/ownership/cell-smart.md
Normal file
4
src/doc/style/ownership/cell-smart.md
Normal 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).
|
62
src/doc/style/ownership/constructors.md
Normal file
62
src/doc/style/ownership/constructors.md
Normal 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.
|
22
src/doc/style/ownership/destructors.md
Normal file
22
src/doc/style/ownership/destructors.md
Normal 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.
|
12
src/doc/style/ownership/raii.md
Normal file
12
src/doc/style/ownership/raii.md
Normal 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`
|
7
src/doc/style/platform.md
Normal file
7
src/doc/style/platform.md
Normal 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.
|
19
src/doc/style/safety/README.md
Normal file
19
src/doc/style/safety/README.md
Normal 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.
|
81
src/doc/style/safety/lib-guarantees.md
Normal file
81
src/doc/style/safety/lib-guarantees.md
Normal 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.
|
22
src/doc/style/safety/unsafe.md
Normal file
22
src/doc/style/safety/unsafe.md
Normal 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.
|
5
src/doc/style/style/README.md
Normal file
5
src/doc/style/style/README.md
Normal 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
|
77
src/doc/style/style/braces.md
Normal file
77
src/doc/style/style/braces.md
Normal 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,
|
||||||
|
}
|
||||||
|
```
|
87
src/doc/style/style/comments.md
Normal file
87
src/doc/style/style/comments.md
Normal 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...
|
||||||
|
```
|
13
src/doc/style/style/features.md
Normal file
13
src/doc/style/style/features.md
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
50
src/doc/style/style/imports.md
Normal file
50
src/doc/style/style/imports.md
Normal 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.
|
115
src/doc/style/style/naming/README.md
Normal file
115
src/doc/style/style/naming/README.md
Normal 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.
|
69
src/doc/style/style/naming/containers.md
Normal file
69
src/doc/style/style/naming/containers.md
Normal 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
|
||||||
|
```
|
32
src/doc/style/style/naming/conversions.md
Normal file
32
src/doc/style/style/naming/conversions.md
Normal 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?
|
32
src/doc/style/style/naming/iterators.md
Normal file
32
src/doc/style/style/naming/iterators.md
Normal 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`.
|
34
src/doc/style/style/naming/ownership.md
Normal file
34
src/doc/style/style/naming/ownership.md
Normal 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.
|
3
src/doc/style/style/optional.md
Normal file
3
src/doc/style/style/optional.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
*
|
||||||
|
|
||||||
|
*
|
14
src/doc/style/style/organization.md
Normal file
14
src/doc/style/style/organization.md
Normal 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.
|
133
src/doc/style/style/whitespace.md
Normal file
133
src/doc/style/style/whitespace.md
Normal 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;
|
||||||
|
```
|
5
src/doc/style/testing/README.md
Normal file
5
src/doc/style/testing/README.md
Normal 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?
|
30
src/doc/style/testing/unit.md
Normal file
30
src/doc/style/testing/unit.md
Normal 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
5
src/doc/style/todo.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
* [Containers and iteration]()
|
||||||
|
* [The visitor pattern]()
|
||||||
|
* [Concurrency]()
|
||||||
|
* [Documentation]()
|
||||||
|
* [Macros]()
|
Loading…
Add table
Add a link
Reference in a new issue