| name | rust-idioms |
| description | Rust Idioms guidance for Fortress Rollback. Use when Writing idiomatic Rust, implementing traits, error handling patterns. |
Rust Idioms
Borrowed Types for Arguments
Accept &str or &[T] instead of &String or &Vec<T>:
fn process(data: &str) -> bool { }
Constructors and Default
impl Config {
pub fn new() -> Self { Self { timeout: Duration::from_secs(30), retries: 3 } }
}
impl Default for Config {
fn default() -> Self { Self::new() }
}
String Building
Use format!() for readability; use String::with_capacity() + push_str in hot loops.
mem::take() and mem::replace()
Move values out of &mut references without cloning:
let owned_data = mem::take(data);
let old = mem::replace(buffer, Vec::with_capacity(1024));
RAII / Finalisation in Destructors
Put cleanup in Drop to ensure it runs regardless of exit path. Destructors must not panic.
On-Stack Dynamic Dispatch (Rust 1.79+)
let reader: &mut dyn Read = if use_stdin { &mut io::stdin() } else { &mut file };
#[non_exhaustive] for Extensibility
Use on public enums/structs that may gain variants/fields in semver-compatible releases.
Explicit Closure Captures
let closure = {
let data = Rc::clone(&data);
move || { println!("{:?}", data); }
};
Return Consumed Argument on Error
pub struct SendError<T>(pub T);
pub fn send<T>(value: T) -> Result<(), SendError<T>> { }
Naming Conventions (RFC 430)
| Item | Convention | Example |
|---|
| Types, Traits | UpperCamelCase | HashMap |
| Functions, Methods | snake_case | get_value |
| Constants | SCREAMING_SNAKE_CASE | MAX_SIZE |
Conversion prefixes:
| Prefix | Cost | Ownership |
|---|
as_ | Free | &T -> &U |
to_ | Expensive | &T -> U |
into_ | Variable | T -> U (consumes) |
Implement Common Traits Eagerly
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub struct PlayerId(u32);
Do not duplicate trait functionality with standalone methods.
Meaningful Error Types
Never use () as error type. Error messages: lowercase, no trailing punctuation.
Sealed Traits
mod private { pub trait Sealed {} }
pub trait Protocol: private::Sealed { }
Generic Functions (C-GENERIC)
Accept impl AsRef<Path> instead of &PathBuf.
Iteration Patterns for Copy Types
for handle in handles { session.add_local_input(handle, input)?; }
for (i, handle) in handles.into_iter().enumerate() { }
for &handle in handles.iter() { process(handle); }
let count = handles.len();
| Pattern | Loop Variable | Use When |
|---|
for x in collection | Owned T | Copy types, consuming OK |
for x in collection.into_iter() | Owned T | Chaining .enumerate(), .filter() |
for x in &collection / .iter() | &T | Need collection after loop, non-Copy |
for &x in collection.iter() | Owned T | Copy types when borrowing collection |
Option .copied() Pattern
Use .copied() to convert Option<&T> to Option<T> for Copy types:
let byte: u8 = data.get(index).copied().ok_or(Error::OutOfBounds)?;
let owned_values: Vec<u8> = slice.iter().copied().collect();
| Situation | Pattern |
|---|
Safe indexing with ? | .get(i).copied().ok_or(err)? |
| Default on missing | .get(i).copied().unwrap_or(default) |
| Match with value | Some(&val) => ... |
| Iterator of values | .iter().copied() |
| Non-Copy types | .get(i).cloned() |
Quick Decision Guide
| Situation | Pattern |
|---|
| Many optional constructor params | Builder |
| Wrap primitive for type safety | Newtype |
| Ensure cleanup on scope exit | RAII Guard |
| Interchangeable algorithms | Strategy (trait or closure) |
| Queueable/undoable operations | Command |
| Operations on heterogeneous tree | Visitor |
| Compile-time state machine | Type State |
Need &str not &String | Borrowed Types |
| Fields need independent borrowing | Struct Decomposition |