mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-26 08:44:35 +00:00
rollup merge of #22331: steveklabnik/guidelines
Fixes #19315 r? @aturon
This commit is contained in:
commit
25ccf3c0da
@ -73,7 +73,7 @@ RUSTBOOK = $(RPATH_VAR2_T_$(CFG_BUILD)_H_$(CFG_BUILD)) $(RUSTBOOK_EXE)
|
||||
|
||||
D := $(S)src/doc
|
||||
|
||||
DOC_TARGETS := trpl
|
||||
DOC_TARGETS := trpl style
|
||||
COMPILER_DOC_TARGETS :=
|
||||
DOC_L10N_TARGETS :=
|
||||
|
||||
@ -275,3 +275,9 @@ trpl: doc/book/index.html
|
||||
doc/book/index.html: $(RUSTBOOK_EXE) $(wildcard $(S)/src/doc/trpl/*.md) | doc/
|
||||
$(Q)rm -rf doc/book
|
||||
$(Q)$(RUSTBOOK) build $(S)src/doc/trpl doc/book
|
||||
|
||||
style: doc/style/index.html
|
||||
|
||||
doc/style/index.html: $(RUSTBOOK_EXE) $(wildcard $(S)/src/doc/style/*.md) | doc/
|
||||
$(Q)rm -rf doc/style
|
||||
$(Q)$(RUSTBOOK) build $(S)src/doc/style doc/style
|
||||
|
64
src/doc/style/README.md
Normal file
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…
Reference in New Issue
Block a user