From 6ac851e718c7a1beab3f60b890d9067f44d6b100 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Wed, 9 Dec 2015 10:59:30 +1300 Subject: [PATCH] Add the extensibility by privacy pattern (And change the concatenation pattern to say something different about perf) --- README.md | 2 +- idioms/concat-format.md | 7 ++++++- idioms/priv-extend.md | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 idioms/priv-extend.md diff --git a/README.md b/README.md index 4f1e70e..72761a3 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ language. * [Constructor](idioms/ctor.md) * [Concatenating strings with `format!`](idioms/concat-format.md) -* TODO private field to indicate extensibility +* [Privacy for extensibility](idioms/priv-extend.md) * TODO trait to separate visibility of methods from visibility of data (https://github.com/sfackler/rust-postgres/blob/master/src/lib.rs#L1400) * [Collections are smart pointers](idioms/deref.md) * TODO leak amplification ("Vec::drain sets the Vec's len to 0 prematurely so that mem::forgetting Drain "only" mem::forgets more stuff. instead of exposing uninitialized memory or having to update the len on every iteration") diff --git a/idioms/concat-format.md b/idioms/concat-format.md index a90af1e..c8fbecb 100644 --- a/idioms/concat-format.md +++ b/idioms/concat-format.md @@ -27,4 +27,9 @@ fn say_hello(name: &str) -> String { ## Advantages Using `format!` is usually the most succinct and readable way to combine strings. -In nearly all cases it will generate optimal code in terms of performance too. + +## Disadvantages + +It is usually not the most efficient way to combine strings - a series of `push` +operations on a mutable string is usually the most efficient (especially if the +string has been pre-allocated to the expected size). diff --git a/idioms/priv-extend.md b/idioms/priv-extend.md new file mode 100644 index 0000000..4cf42fe --- /dev/null +++ b/idioms/priv-extend.md @@ -0,0 +1,35 @@ +# Privacy for extensibility + +## Description + +Use a private field to ensure that a struct is extensible without breaking +stability guarantees. + + +## Example + +```rust +mod a { + // Public struct. + pub struct S { + pub foo: i32, + // Private field. + bar: i32, + } +} + +fn main(s: a::S) { + // Because S::bar is private, it cannot be named here and we must use `..` + // in the pattern. + let a::S { foo: _, ..} = s; +} + +``` + +## Discussion + +Adding a field to a struct is a mostly backwards compatible change. However, if a client uses a pattern to deconstruct a struct instance, they might name all the fields in the struct and adding a new one would break that pattern. The client could name some of the fields and use `..` in the pattern, in which case adding another field is backwards compatible. Making at least one of the struct's fields private forces clients to use the latter form of patterns, ensuring that the struct is future-proof. + +The downside of this approach is that you might need to add an otherwise unneeded field to the struct. You can use the `()` type so that there is no runtime overhead and can add the annotation `#[allow(dead_code)]` to the struct to avoid the unused field warning. + +If Rust allowed private variants of enums, we could use the same trick to make adding a variant to an enum backwards compatible. The problem there is exhaustive match expressions. A private variant would force clients to have a `_` wildcard pattern.