diff --git a/mk/docs.mk b/mk/docs.mk index 2a5a847d2a425..04357e8fbae19 100644 --- a/mk/docs.mk +++ b/mk/docs.mk @@ -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 diff --git a/src/doc/style/README.md b/src/doc/style/README.md new file mode 100644 index 0000000000000..9b328b5d393c5 --- /dev/null +++ b/src/doc/style/README.md @@ -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? diff --git a/src/doc/style/SUMMARY.md b/src/doc/style/SUMMARY.md new file mode 100644 index 0000000000000..41bc08f229e7c --- /dev/null +++ b/src/doc/style/SUMMARY.md @@ -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) diff --git a/src/doc/style/changing/README.md b/src/doc/style/changing/README.md new file mode 100644 index 0000000000000..38e76f6970c81 --- /dev/null +++ b/src/doc/style/changing/README.md @@ -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. diff --git a/src/doc/style/changing/post-1-0.md b/src/doc/style/changing/post-1-0.md new file mode 100644 index 0000000000000..7ac1c837c071f --- /dev/null +++ b/src/doc/style/changing/post-1-0.md @@ -0,0 +1,12 @@ +% Post-1.0 changes + +### Higher-kinded types + +* A trait encompassing both `Iterable` for some fixed `T` and + `FromIterator` 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. diff --git a/src/doc/style/changing/pre-1-0.md b/src/doc/style/changing/pre-1-0.md new file mode 100644 index 0000000000000..adadfe31a59d1 --- /dev/null +++ b/src/doc/style/changing/pre-1-0.md @@ -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 diff --git a/src/doc/style/changing/unclear.md b/src/doc/style/changing/unclear.md new file mode 100644 index 0000000000000..e4b8a98e1a162 --- /dev/null +++ b/src/doc/style/changing/unclear.md @@ -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 diff --git a/src/doc/style/errors/README.md b/src/doc/style/errors/README.md new file mode 100644 index 0000000000000..444da26ff8fed --- /dev/null +++ b/src/doc/style/errors/README.md @@ -0,0 +1,3 @@ +% Errors + +> **[FIXME]** Add some general text here. diff --git a/src/doc/style/errors/ergonomics.md b/src/doc/style/errors/ergonomics.md new file mode 100644 index 0000000000000..33f1e82b187e7 --- /dev/null +++ b/src/doc/style/errors/ergonomics.md @@ -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. diff --git a/src/doc/style/errors/handling.md b/src/doc/style/errors/handling.md new file mode 100644 index 0000000000000..cc5b5b475769c --- /dev/null +++ b/src/doc/style/errors/handling.md @@ -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] diff --git a/src/doc/style/errors/propagation.md b/src/doc/style/errors/propagation.md new file mode 100644 index 0000000000000..0a347cd577b90 --- /dev/null +++ b/src/doc/style/errors/propagation.md @@ -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. diff --git a/src/doc/style/errors/signaling.md b/src/doc/style/errors/signaling.md new file mode 100644 index 0000000000000..95db4f8afa03c --- /dev/null +++ b/src/doc/style/errors/signaling.md @@ -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` 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. diff --git a/src/doc/style/features/README.md b/src/doc/style/features/README.md new file mode 100644 index 0000000000000..09657503d20d1 --- /dev/null +++ b/src/doc/style/features/README.md @@ -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). diff --git a/src/doc/style/features/crates.md b/src/doc/style/features/crates.md new file mode 100644 index 0000000000000..4748b05f17f74 --- /dev/null +++ b/src/doc/style/features/crates.md @@ -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" diff --git a/src/doc/style/features/functions-and-methods/README.md b/src/doc/style/features/functions-and-methods/README.md new file mode 100644 index 0000000000000..2dcfc382d0baf --- /dev/null +++ b/src/doc/style/features/functions-and-methods/README.md @@ -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. diff --git a/src/doc/style/features/functions-and-methods/convenience.md b/src/doc/style/features/functions-and-methods/convenience.md new file mode 100644 index 0000000000000..69fd3772a761f --- /dev/null +++ b/src/doc/style/features/functions-and-methods/convenience.md @@ -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. diff --git a/src/doc/style/features/functions-and-methods/input.md b/src/doc/style/features/functions-and-methods/input.md new file mode 100644 index 0000000000000..b0912ea0203dc --- /dev/null +++ b/src/doc/style/features/functions-and-methods/input.md @@ -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) -> Box { ... } +``` + +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>(c: T) { ... } +``` + +over any of + +```rust +fn foo(c: &[int]) { ... } +fn foo(c: &Vec) { ... } +fn foo(c: &SomeOtherCollection) { ... } +``` + +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 +``` + +(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`? diff --git a/src/doc/style/features/functions-and-methods/output.md b/src/doc/style/features/functions-and-methods/output.md new file mode 100644 index 0000000000000..a83e2b76bcb7f --- /dev/null +++ b/src/doc/style/features/functions-and-methods/output.md @@ -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) -> Result> +``` + +over + +```rust +fn from_utf8_owned(vv: Vec) -> Option +``` + +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. diff --git a/src/doc/style/features/let.md b/src/doc/style/features/let.md new file mode 100644 index 0000000000000..87117a20d7a49 --- /dev/null +++ b/src/doc/style/features/let.md @@ -0,0 +1,103 @@ +% Let binding + +### Always separately bind RAII guards. [FIXME: needs RFC] + +Prefer + +```rust +fn use_mutex(m: sync::mutex::Mutex) { + let guard = m.lock(); + do_work(guard); + drop(guard); // unlock the lock + // do other work +} +``` + +over + +```rust +fn use_mutex(m: sync::mutex::Mutex) { + 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::>() +``` + +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. diff --git a/src/doc/style/features/loops.md b/src/doc/style/features/loops.md new file mode 100644 index 0000000000000..b144825f98183 --- /dev/null +++ b/src/doc/style/features/loops.md @@ -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` diff --git a/src/doc/style/features/match.md b/src/doc/style/features/match.md new file mode 100644 index 0000000000000..131e0fad79a92 --- /dev/null +++ b/src/doc/style/features/match.md @@ -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(...) => ... +} +~~~~ + + + + + diff --git a/src/doc/style/features/modules.md b/src/doc/style/features/modules.md new file mode 100644 index 0000000000000..04aae226f72b5 --- /dev/null +++ b/src/doc/style/features/modules.md @@ -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. diff --git a/src/doc/style/features/traits/README.md b/src/doc/style/features/traits/README.md new file mode 100644 index 0000000000000..1893db24466fa --- /dev/null +++ b/src/doc/style/features/traits/README.md @@ -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. diff --git a/src/doc/style/features/traits/common.md b/src/doc/style/features/traits/common.md new file mode 100644 index 0000000000000..48c37eabcaaa1 --- /dev/null +++ b/src/doc/style/features/traits/common.md @@ -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. diff --git a/src/doc/style/features/traits/extensions.md b/src/doc/style/features/traits/extensions.md new file mode 100644 index 0000000000000..fc3a03c01f5a1 --- /dev/null +++ b/src/doc/style/features/traits/extensions.md @@ -0,0 +1,7 @@ +% Using traits to add extension methods + +> **[FIXME]** Elaborate. + +### Consider using default methods rather than extension traits **[FIXME]** + +> **[FIXME]** Elaborate. diff --git a/src/doc/style/features/traits/generics.md b/src/doc/style/features/traits/generics.md new file mode 100644 index 0000000000000..ab4f9cb157961 --- /dev/null +++ b/src/doc/style/features/traits/generics.md @@ -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>(iterator: T) -> SomeCollection +``` + +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(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` 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. diff --git a/src/doc/style/features/traits/objects.md b/src/doc/style/features/traits/objects.md new file mode 100644 index 0000000000000..38494a9b9bc3c --- /dev/null +++ b/src/doc/style/features/traits/objects.md @@ -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]) -> Frame { + ... + } +} + +fn make_gui() -> Box { + let b: Box = box Button::new(...); + let l: Box = box Label::new(...); + + box Frame::new([b, l]) as Box +} +``` + +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. diff --git a/src/doc/style/features/traits/overloading.md b/src/doc/style/features/traits/overloading.md new file mode 100644 index 0000000000000..d7482c9619072 --- /dev/null +++ b/src/doc/style/features/traits/overloading.md @@ -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. diff --git a/src/doc/style/features/traits/reuse.md b/src/doc/style/features/traits/reuse.md new file mode 100644 index 0000000000000..6735023ae6800 --- /dev/null +++ b/src/doc/style/features/traits/reuse.md @@ -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`. diff --git a/src/doc/style/features/types/README.md b/src/doc/style/features/types/README.md new file mode 100644 index 0000000000000..c675eb581c6ae --- /dev/null +++ b/src/doc/style/features/types/README.md @@ -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/) diff --git a/src/doc/style/features/types/conversions.md b/src/doc/style/features/types/conversions.md new file mode 100644 index 0000000000000..f0f230f57e557 --- /dev/null +++ b/src/doc/style/features/types/conversions.md @@ -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. diff --git a/src/doc/style/features/types/newtype.md b/src/doc/style/features/types/newtype.md new file mode 100644 index 0000000000000..60c17fc2a52e2 --- /dev/null +++ b/src/doc/style/features/types/newtype.md @@ -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>>`. 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(Enumerate>>); +impl Iterator<(uint, T)> for MyTransformResult { ... } + +fn my_transform>(iter: Iter) -> MyTransformResult { + ... +} +``` + +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. diff --git a/src/doc/style/ownership/README.md b/src/doc/style/ownership/README.md new file mode 100644 index 0000000000000..11bdb03a3a818 --- /dev/null +++ b/src/doc/style/ownership/README.md @@ -0,0 +1,3 @@ +% Ownership and resource management + +> **[FIXME]** Add general remarks about ownership/resources here. diff --git a/src/doc/style/ownership/builders.md b/src/doc/style/ownership/builders.md new file mode 100644 index 0000000000000..94eda59b95b65 --- /dev/null +++ b/src/doc/style/ownership/builders.md @@ -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, + cwd: Option, + // 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 { + ... + } +} +``` + +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) -> 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. diff --git a/src/doc/style/ownership/cell-smart.md b/src/doc/style/ownership/cell-smart.md new file mode 100644 index 0000000000000..cd027cc4aaffc --- /dev/null +++ b/src/doc/style/ownership/cell-smart.md @@ -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). diff --git a/src/doc/style/ownership/constructors.md b/src/doc/style/ownership/constructors.md new file mode 100644 index 0000000000000..b4a1147315679 --- /dev/null +++ b/src/doc/style/ownership/constructors.md @@ -0,0 +1,62 @@ +% Constructors + +### Define constructors as static, inherent methods. [FIXME: needs RFC] + +In Rust, "constructors" are just a convention: + +```rust +impl Vec { + pub fn new() -> Vec { ... } +} +``` + +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. diff --git a/src/doc/style/ownership/destructors.md b/src/doc/style/ownership/destructors.md new file mode 100644 index 0000000000000..8f58aa6c6d2f1 --- /dev/null +++ b/src/doc/style/ownership/destructors.md @@ -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. diff --git a/src/doc/style/ownership/raii.md b/src/doc/style/ownership/raii.md new file mode 100644 index 0000000000000..244e8096a1a2f --- /dev/null +++ b/src/doc/style/ownership/raii.md @@ -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` diff --git a/src/doc/style/platform.md b/src/doc/style/platform.md new file mode 100644 index 0000000000000..d29d060b69461 --- /dev/null +++ b/src/doc/style/platform.md @@ -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. diff --git a/src/doc/style/safety/README.md b/src/doc/style/safety/README.md new file mode 100644 index 0000000000000..1ac6e704d23eb --- /dev/null +++ b/src/doc/style/safety/README.md @@ -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. diff --git a/src/doc/style/safety/lib-guarantees.md b/src/doc/style/safety/lib-guarantees.md new file mode 100644 index 0000000000000..aa87223383a10 --- /dev/null +++ b/src/doc/style/safety/lib-guarantees.md @@ -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` 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. diff --git a/src/doc/style/safety/unsafe.md b/src/doc/style/safety/unsafe.md new file mode 100644 index 0000000000000..a8a50af044c29 --- /dev/null +++ b/src/doc/style/safety/unsafe.md @@ -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. diff --git a/src/doc/style/style/README.md b/src/doc/style/style/README.md new file mode 100644 index 0000000000000..87449710543c0 --- /dev/null +++ b/src/doc/style/style/README.md @@ -0,0 +1,5 @@ +% Style + +This section gives a set of strict rules for styling Rust code. + +> **[FIXME]** General remarks about the style guidelines diff --git a/src/doc/style/style/braces.md b/src/doc/style/style/braces.md new file mode 100644 index 0000000000000..0f61bac9fd229 --- /dev/null +++ b/src/doc/style/style/braces.md @@ -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, +} +``` diff --git a/src/doc/style/style/comments.md b/src/doc/style/style/comments.md new file mode 100644 index 0000000000000..347750ce6020d --- /dev/null +++ b/src/doc/style/style/comments.md @@ -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... +``` diff --git a/src/doc/style/style/features.md b/src/doc/style/style/features.md new file mode 100644 index 0000000000000..f73517c2b9c3b --- /dev/null +++ b/src/doc/style/style/features.md @@ -0,0 +1,13 @@ +## `return` [FIXME: needs RFC] + +Terminate `return` statements with semicolons: + +``` rust +fn foo(bar: int) -> Option { + if some_condition() { + return None; + } + + ... +} +``` diff --git a/src/doc/style/style/imports.md b/src/doc/style/style/imports.md new file mode 100644 index 0000000000000..207a3fd7f8d16 --- /dev/null +++ b/src/doc/style/style/imports.md @@ -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. diff --git a/src/doc/style/style/naming/README.md b/src/doc/style/style/naming/README.md new file mode 100644 index 0000000000000..9d78721ad3644 --- /dev/null +++ b/src/doc/style/style/naming/README.md @@ -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` | + +

+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` | `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. diff --git a/src/doc/style/style/naming/containers.md b/src/doc/style/style/naming/containers.md new file mode 100644 index 0000000000000..04204f0f88aec --- /dev/null +++ b/src/doc/style/style/naming/containers.md @@ -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 + +// Lookup element, failing if it is not found: +impl Index for Container { ... } +impl IndexMut 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`: + +```rust +// Extract contents or fail if not available +fn assert(self) -> V +fn expect(self, &str) -> V +``` + +For `Result`: + +```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 +``` diff --git a/src/doc/style/style/naming/conversions.md b/src/doc/style/style/naming/conversions.md new file mode 100644 index 0000000000000..0287919c78aae --- /dev/null +++ b/src/doc/style/style/naming/conversions.md @@ -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 | + +

+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`, 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? diff --git a/src/doc/style/style/naming/iterators.md b/src/doc/style/style/naming/iterators.md new file mode 100644 index 0000000000000..38138b5e39d3a --- /dev/null +++ b/src/doc/style/style/naming/iterators.md @@ -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 +``` + +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`. diff --git a/src/doc/style/style/naming/ownership.md b/src/doc/style/style/naming/ownership.md new file mode 100644 index 0000000000000..32cd8a1595afb --- /dev/null +++ b/src/doc/style/style/naming/ownership.md @@ -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. diff --git a/src/doc/style/style/optional.md b/src/doc/style/style/optional.md new file mode 100644 index 0000000000000..d3c2178cc993f --- /dev/null +++ b/src/doc/style/style/optional.md @@ -0,0 +1,3 @@ +* + +* diff --git a/src/doc/style/style/organization.md b/src/doc/style/style/organization.md new file mode 100644 index 0000000000000..85065406d761c --- /dev/null +++ b/src/doc/style/style/organization.md @@ -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. diff --git a/src/doc/style/style/whitespace.md b/src/doc/style/style/whitespace.md new file mode 100644 index 0000000000000..b21b280dff0d7 --- /dev/null +++ b/src/doc/style/style/whitespace.md @@ -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( + 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; +``` diff --git a/src/doc/style/testing/README.md b/src/doc/style/testing/README.md new file mode 100644 index 0000000000000..a21f69414d326 --- /dev/null +++ b/src/doc/style/testing/README.md @@ -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? diff --git a/src/doc/style/testing/unit.md b/src/doc/style/testing/unit.md new file mode 100644 index 0000000000000..813660d8fdfb9 --- /dev/null +++ b/src/doc/style/testing/unit.md @@ -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!` diff --git a/src/doc/style/todo.md b/src/doc/style/todo.md new file mode 100644 index 0000000000000..28ef2a1832d8b --- /dev/null +++ b/src/doc/style/todo.md @@ -0,0 +1,5 @@ +* [Containers and iteration]() +* [The visitor pattern]() +* [Concurrency]() +* [Documentation]() +* [Macros]()