PingCAP Style Guide

Guide to writing idiomatic code at PingCAP and in the TiKV project.

Project maintained by pingcap Hosted on GitHub Pages — Theme by mattgraham

Data structures

For generic data types, prefer to use the most flexible bounds. That usually means putting strict bounds only on impls, not on the concrete datatype, and may mean splitting impls into smaller blocks. The generic parameters on the concrete type should only have bounds if they are intrinsic to the datatype itself (which includes lifetime bounds, ?Sized, where the bound is required to name an associated type, or the bound is required by a Drop impl). Generic parameters in concrete datatypes should never have derivable bounds.

Rationale: makes datatypes most flexible, reduces code duplication, and makes types more backwards compatible. See this section of the API guide for more details.

Avoid default generics, unless the default is used in the vast majority of situations (e.g., the default is only overridden for testing, or in very exceptional cases, such as supplying a hasher to a hashtable). Rationale: having an explicit type often improves comprehensability of code and outweighs the convenience of skipping a generic parameter).

Use types to enforce invariants. Take advantage of structs, tuples, and enums to define the valid states of an object. Use associated types to enforce relationships between types. Where possible, make invalid objects impossible.

Use type aliases (type) to provide convenience versions of common generic types (e.g., type Result<T> = Result<T, MyErr>;).




Rationale: named types and fields nearly always improve the readability of code.


Only use unions for FFI to mirror a C enum. In Rust code prefer an enum.

Builder pattern

A common way to construct concrete data with complex internals is the builder pattern. It is recommended to use the builder pattern to avoid a large number of constructor functions (or complex constructor functions with many Options), but avoid using them instead of one or two simple constructor functions.

<< Modules and crates | Implementing traits >>