Improve readability of CODE_STYLE.md

Former-commit-id: 36f0226751
This commit is contained in:
Maybe Waffle 2022-09-29 19:01:03 +04:00
parent 1af8b0f904
commit 45c1324cb3

View file

@ -1,100 +1,101 @@
# 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.
This is a description of a coding style that every contributor must follow.
Please, read the whole document before you start pushing code.
## Generics
Generics are always written with `where`.
Bad:
All trait bounds should be written in `where`
```rust
pub fn new<N: Into<String>,
T: Into<String>,
P: Into<InputFile>,
E: Into<String>>
// 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:
```rust
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> { ... }
// GOOD
impl<T> Trait for Wrap<T>
where
T: Trait
{ ... }
// BAD
impl<T: Trait> Trait for Wrap<T> { ... }
```
## Comments
1. Comments 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.
**Rationale:** `where` clauses are easier to read when there are a lot of bounds, uniformity.
Bad:
## Documentation comments
```rust
/// this function make request to telegram
pub fn make_request(url: &str) -> String { ... }
```
1. Documentation must describe what your code does and mustn't describe how your code does it and bla-bla-bla.
2. Be sure that your comments follow the grammar, including punctuation, the first capital letter and so on.
```rust
// 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 { ... }
```
3. Do not use ending punctuation in short list items (usually containing just one phrase or sentence).
```md
<!-- GOOD -->
- Handle different kinds of Update
- Pass dependencies to handlers
- Disable a default Ctrl-C handling
Good:
<!-- BAD -->
- Handle different kinds of Update.
- Pass dependencies to handlers.
- Disable a default Ctrl-C handling.
```rust
/// This function makes a request to Telegram.
pub fn make_request(url: &str) -> String { ... }
```
<!-- BAD -->
- Handle different kinds of Update;
- Pass dependencies to handlers;
- Disable a default Ctrl-C handling;
```
3. Link resources in your comments when possible, for example:
```rust
/// 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
```
2. Do not use ending punctuation in short list items (usually containing just one phrase or sentence). Bad:
## Use `Self` where possible
```md
- Handle different kinds of Update.
- Pass dependencies to handlers.
- Disable a default Ctrl-C handling.
```
Bad:
```md
- Handle different kinds of Update;
- Pass dependencies to handlers;
- Disable a default Ctrl-C handling.
```
Good:
```md
- Handle different kinds of Update
- Pass dependencies to handlers
- Disable a default Ctrl-C handling
```
3. Link resources in your comments when possible:
```rust
/// 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
#[cfg(feature = "unstable-stream")]
pub async fn download_file_stream(
&self,
path: &str,
) -> Result<impl Stream<Item = Result<Bytes, reqwest::Error>>, reqwest::Error>
{
download_file_stream(&self.client, &self.token, path).await
}
```
## Use Self where possible
Bad:
When referring to the type for which block is implemented, prefer using `Self`, rather than the name of the type.
```rust
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"),
@ -102,50 +103,96 @@ impl ErrorKind {
}
}
```
Good:
```rust
impl ErrorKind {
fn print(&self) {
Self::Io => println!("Io"),
Self::Network => println!("Network"),
Self::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
```rust
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.
```rust
impl User {
// GOOD
#[must_use]
fn full_name(&self) -> String {
format!("{} {}", user.first_name, user.last_name)
}
}
```
<details>
<summary>More examples</summary>
Bad:
**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>!(...)`.
```rust
impl<'a> AnswerCallbackQuery<'a> {
pub(crate) fn new<C>(bot: &'a Bot, callback_query_id: C) -> AnswerCallbackQuery<'a>
where
C: Into<String>, { ... }
// GOOD
log::warn!("Everything is on fire");
// BAD
use log::warn;
warn!("Everything is on fire");
```
Good:
```rust
impl<'a> AnswerCallbackQuery<'a> {
pub(crate) fn new<C>(bot: &'a Bot, callback_query_id: C) -> Self
where
C: Into<String>, { ... }
```
</details>
## Naming
1. Avoid unnecessary duplication (`Message::message_id` -> `Message::id` using `#[serde(rename = "message_id")]`).
2. Use a generic parameter name `S` for streams, `Fut` for futures, `F` for functions (where possible).
## Deriving
1. Derive `Copy`, `Eq`, `Hash`, `PartialEq`, `Clone`, `Debug` for public types when possible (note: if the default `Debug` implementation is weird, you should manually implement it by yourself).
2. Derive `Default` when there is an algorithm to get a default value for your type.
## Misc
1. Use `Into<...>` only where there exists at least one conversion **and** it will be logically to use.
2. Always mark a function as `#[must_use]` if its return value **must** be used.
3. `Box::pin(async [move] { ... })` instead of `async [move] { ... }.boxed()`.
4. Always write `log::<op>!(...)` instead of importing `use log::<op>;` and invoking `<op>!(...)`. For example, write `log::info!("blah")`.
**Rationale:** uniformity, it's clearer which log crate is used.