mirror of
https://github.com/aljazceru/cdk.git
synced 2025-12-25 08:35:09 +01:00
237 lines
4.3 KiB
Markdown
237 lines
4.3 KiB
Markdown
# 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`:
|
|
|
|
```rust
|
|
// 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 { ... }
|
|
```
|
|
|
|
```rust
|
|
// GOOD
|
|
impl<T> Trait for Wrap<T>
|
|
where
|
|
T: Trait
|
|
{ ... }
|
|
|
|
// BAD
|
|
impl<T: Trait> Trait for Wrap<T> { ... }
|
|
```
|
|
|
|
## Use `Self` where possible
|
|
|
|
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"),
|
|
ErrorKind::Json => println!("Json"),
|
|
}
|
|
}
|
|
```
|
|
|
|
```rust
|
|
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.
|
|
|
|
## Deriving traits (apply only to libraries)
|
|
|
|
Derive `Debug`, `Clone`, `Copy`, `PartialEq`, `Eq` and `Hash` for public types when possible (in this order).
|
|
|
|
**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.
|
|
|
|
## Full paths for logging
|
|
|
|
Always write `tracing::<op>!(...)` instead of importing `use tracing::<op>;` and invoking `<op>!(...)`.
|
|
|
|
```rust
|
|
// GOOD
|
|
tracing::warn!("Everything is on fire");
|
|
|
|
// BAD
|
|
use tracing::warn;
|
|
|
|
warn!("Everything is on fire");
|
|
```
|
|
|
|
**Rationale:**
|
|
- Less polluted import blocks
|
|
- Uniformity
|
|
|
|
## `&str` -> `String` conversion
|
|
|
|
Prefer using `.to_string()` or `.to_owned()`, rather than `.into()`, `String::from`, etc.
|
|
|
|
**Rationale:** uniformity, intent clarity.
|
|
|
|
## Order of imports
|
|
|
|
```rust
|
|
// First core, alloc and/or std
|
|
use core::fmt;
|
|
use std::{...};
|
|
|
|
// Second, external crates (both crates.io crates and other rust-analyzer crates).
|
|
use crate_foo::{ ... };
|
|
use crate_bar::{ ... };
|
|
|
|
// If applicable, the current sub-modules
|
|
mod x;
|
|
mod y;
|
|
|
|
// Finally, the internal crate modules and submodules
|
|
use crate::{};
|
|
use super::{};
|
|
use self::y::Y;
|
|
```
|
|
|
|
## Import Style
|
|
|
|
When implementing traits from `core::fmt`/`std::fmt` import the module:
|
|
|
|
```rust
|
|
// GOOD
|
|
use core::fmt;
|
|
|
|
impl fmt::Display for RenameError {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { .. }
|
|
}
|
|
|
|
// BAD
|
|
impl core::fmt::Display for RenameError {
|
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { .. }
|
|
}
|
|
```
|
|
|
|
When imports sub-modules:
|
|
|
|
```rust
|
|
// GOOD
|
|
mod x;
|
|
|
|
use self::x::Y;
|
|
|
|
// BAD
|
|
mod x;
|
|
|
|
use x::Y;
|
|
```
|
|
|
|
## If-let
|
|
|
|
Avoid the `if let ... { } else { }` construct if possible, use `match` instead:
|
|
|
|
```rust
|
|
// GOOD
|
|
match ctx.expected_type.as_ref() {
|
|
Some(expected_type) => completion_ty == expected_type && !expected_type.is_unit(),
|
|
None => false,
|
|
}
|
|
|
|
// BAD
|
|
if let Some(expected_type) = ctx.expected_type.as_ref() {
|
|
completion_ty == expected_type && !expected_type.is_unit()
|
|
} else {
|
|
false
|
|
}
|
|
```
|
|
|
|
Use `if let ... { }` when a match arm is intentionally empty:
|
|
|
|
```rust
|
|
// GOOD
|
|
if let Some(expected_type) = this.as_ref() {
|
|
// Handle it
|
|
}
|
|
|
|
// BAD
|
|
match this.as_ref() {
|
|
Some(expected_type) => {
|
|
// Handle it
|
|
},
|
|
None => (),
|
|
}
|
|
```
|
|
|
|
## Sub-modules
|
|
|
|
Avoid the `mod x { .. }` construct if possible. Instead, crate a file `x.rs` and define it with `mod x;`
|
|
|
|
**This applies to all sub-modules except `tests` and `benches`.**
|
|
|
|
```rust
|
|
// GOOD
|
|
mod x;
|
|
|
|
// BAD
|
|
mod x {
|
|
..
|
|
}
|
|
```
|
|
|
|
```rust
|
|
// GOOD
|
|
#[cfg(test)]
|
|
mod tests {
|
|
..
|
|
}
|
|
|
|
// BAD
|
|
mod tests;
|
|
```
|
|
|
|
```rust
|
|
// GOOD
|
|
#[cfg(bench)]
|
|
mod benches {
|
|
..
|
|
}
|
|
|
|
// BAD
|
|
mod benches;
|
|
```
|