5.1 KiB
Code style
This is a description of a coding style that every contributor must follow. Please, read the whole document before you start pushing code.
Generics
All trait bounds should be written in where
// GOOD
pub fn new<N, T, P, E>(user_id: i32, name: N, title: T, png_sticker: P, emojis: E) -> Self
where
N: Into<String>,
T: Into<String>,
P: Into<InputFile>,
E: Into<String>,
{ ... }
// BAD
pub fn new<N: Into<String>,
T: Into<String>,
P: Into<InputFile>,
E: Into<String>>
(user_id: i32, name: N, title: T, png_sticker: P, emojis: E) -> Self { ... }
// GOOD
impl<T> Trait for Wrap<T>
where
T: Trait
{ ... }
// BAD
impl<T: Trait> Trait for Wrap<T> { ... }
Rationale: where
clauses are easier to read when there are a lot of bounds, uniformity.
Documentation comments
- Documentation must describe what your code does and mustn't describe how your code does it and bla-bla-bla.
- Be sure that your comments follow the grammar, including punctuation, the first capital letter and so on.
// GOOD /// This function makes a request to Telegram. pub fn make_request(url: &str) -> String { ... } // BAD /// this function make request to telegram pub fn make_request(url: &str) -> String { ... }
- Do not use ending punctuation in short list items (usually containing just one phrase or sentence).
<!-- GOOD --> - Handle different kinds of Update - Pass dependencies to handlers - Disable a default Ctrl-C handling <!-- BAD --> - Handle different kinds of Update. - Pass dependencies to handlers. - Disable a default Ctrl-C handling. <!-- BAD --> - Handle different kinds of Update; - Pass dependencies to handlers; - Disable a default Ctrl-C handling;
- Link resources in your comments when possible, for example:
/// Download a file from Telegram. /// /// `path` can be obtained from the [`Bot::get_file`]. /// /// To download into [`AsyncWrite`] (e.g. [`tokio::fs::File`]), see /// [`Bot::download_file`]. /// /// [`Bot::get_file`]: crate::bot::Bot::get_file /// [`AsyncWrite`]: tokio::io::AsyncWrite /// [`tokio::fs::File`]: tokio::fs::File /// [`Bot::download_file`]: crate::Bot::download_file
Use Self
where possible
When referring to the type for which block is implemented, prefer using Self
, rather than the name of the type.
impl ErrorKind {
// GOOD
fn print(&self) {
Self::Io => println!("Io"),
Self::Network => println!("Network"),
Self::Json => println!("Json"),
}
// BAD
fn print(&self) {
ErrorKind::Io => println!("Io"),
ErrorKind::Network => println!("Network"),
ErrorKind::Json => println!("Json"),
}
}
impl<'a> AnswerCallbackQuery<'a> {
// GOOD
fn new<C>(bot: &'a Bot, callback_query_id: C) -> Self
where
C: Into<String>,
{ ... }
// BAD
fn new<C>(bot: &'a Bot, callback_query_id: C) -> AnswerCallbackQuery<'a>
where
C: Into<String>,
{ ... }
}
Rationale: Self
is generally shorter and it's easier to copy-paste code or rename the type.
Avoid duplication in fields names
struct Message {
// GOOD
#[serde(rename = "message_id")]
id: MessageId,
// BAD
message_id: MessageId,
}
Rationale: duplication is unnecessary
Conventional generic names
Use a generic parameter name S
for streams, Fut
for futures, F
for functions (where possible).
Rationale: uniformity.
Deriving traits
Derive Copy
, Clone
, Eq
, PartialEq
, Hash
and Debug
for public types when possible.
Rationale: these traits can be useful for users and can be implemented for most types.
Derive Default
when there is a reasonable default value for the type.
Rationale: Default
plays nicely with generic code (for example mem::take
).
Into
-polymorphism
Use T: Into<Ty>
when this can simplify user code.
I.e. when there are types that implement Into<Ty>
that are likely to be passed to this function.
Rationale: conversions unnecessarily complicate caller code and can be confusing for beginners.
must_use
Always mark a functions as #[must_use]
if they don't have side-effects and the only reason to call them is to get the result.
impl User {
// GOOD
#[must_use]
fn full_name(&self) -> String {
format!("{} {}", user.first_name, user.last_name)
}
}
Rationale: users will get warnings if they forgot to do something with the result, potentially preventing bugs.
Creating boxed futures
Prefer Box::pin(async { ... })
instead of async { ... }.boxed()
.
Rationale: the former is generally formatted better by rustfmt.
Full paths for logging
Always write log::<op>!(...)
instead of importing use log::<op>;
and invoking <op>!(...)
.
// GOOD
log::warn!("Everything is on fire");
// BAD
use log::warn;
warn!("Everything is on fire");
Rationale: uniformity, it's clearer which log crate is used.