mirror of
https://github.com/leonardomso/rust-skills.git
synced 2026-09-14 19:33:21 +08:00
0016d5cfb2
Includes rules for: - Ownership and borrowing patterns - Error handling with thiserror/anyhow - Memory management and allocation - API design following Rust guidelines - Async/Tokio patterns - Performance optimization - Naming conventions - Type safety - Testing strategies - Documentation standards - Project structure - Linting configuration - Common anti-patterns to avoid
4.0 KiB
4.0 KiB
api-impl-into
Accept
impl Into<T>for flexible APIs, implementFrom<T>for conversions
Why It Matters
APIs that accept impl Into<T> are ergonomic—callers can pass the target type directly or any type that converts to it. This reduces boilerplate .into() calls at call sites. Implement From<T> rather than Into<T> because From implies Into through a blanket implementation.
Bad
// Requires exact type - forces callers to convert
fn process_path(path: PathBuf) { ... }
fn set_name(name: String) { ... }
// Caller must convert explicitly
process_path(PathBuf::from("/path/to/file"));
process_path("/path/to/file".to_path_buf()); // Verbose
process_path("/path/to/file".into()); // Explicit
set_name(String::from("Alice"));
set_name("Alice".to_string()); // Verbose
Good
// Accept anything that converts to the target type
fn process_path(path: impl Into<PathBuf>) {
let path = path.into(); // Convert once inside
// ...
}
fn set_name(name: impl Into<String>) {
let name = name.into();
// ...
}
// Callers are ergonomic
process_path("/path/to/file"); // &str converts automatically
process_path(PathBuf::from(".")); // PathBuf works too
set_name("Alice"); // &str
set_name(String::from("Alice")); // String
set_name(format!("User-{}", id)); // String from format!
Implement From, Not Into
struct UserId(u64);
// ✅ Implement From
impl From<u64> for UserId {
fn from(id: u64) -> Self {
UserId(id)
}
}
// Into is automatically provided by blanket impl
let id: UserId = 42u64.into(); // Works!
// ❌ Don't implement Into directly
impl Into<UserId> for u64 {
fn into(self) -> UserId {
UserId(self) // This works but is non-idiomatic
}
}
Common Conversions
// String-like types
fn log_message(msg: impl Into<String>) { ... }
log_message("literal"); // &str
log_message(String::from("own")); // String
log_message(Cow::from("cow")); // Cow<str>
// Path-like types
fn read_file(path: impl AsRef<Path>) { ... } // AsRef for borrowed access
fn write_file(path: impl Into<PathBuf>) { ... } // Into when storing
// Duration
fn set_timeout(duration: impl Into<Duration>) { ... }
set_timeout(Duration::from_secs(5));
// Note: no blanket impl for integers, would need custom wrapper
AsRef vs Into
// AsRef<T>: borrow as &T, no conversion cost
fn count_bytes(data: impl AsRef<[u8]>) -> usize {
data.as_ref().len() // Just borrows, no allocation
}
count_bytes("hello"); // &str -> &[u8]
count_bytes(b"hello"); // &[u8] -> &[u8]
count_bytes(vec![1, 2, 3]); // &Vec<u8> -> &[u8]
// Into<T>: convert to owned T, may allocate
fn store_data(data: impl Into<Vec<u8>>) {
let owned: Vec<u8> = data.into(); // Takes ownership
// ...
}
When NOT to Use impl Into
// ❌ Trait objects need Sized
fn process(handler: impl Into<Box<dyn Handler>>) { }
// Better: just take Box<dyn Handler> directly
// ❌ Recursive types
struct Node {
children: Vec<impl Into<Node>>, // Error: impl Trait not allowed here
}
// ❌ Performance-critical hot paths (minor overhead of trait dispatch)
fn hot_path(value: impl Into<u64>) {
// Consider taking u64 directly if called billions of times
}
// ❌ When you need to name the type
fn returns_impl() -> impl Into<String> { } // Opaque, hard to use
Builder Pattern with Into
struct Config {
name: String,
path: PathBuf,
}
impl Config {
fn new(name: impl Into<String>) -> Self {
Config {
name: name.into(),
path: PathBuf::new(),
}
}
fn path(mut self, path: impl Into<PathBuf>) -> Self {
self.path = path.into();
self
}
}
// Clean builder calls
let config = Config::new("myapp")
.path("/etc/myapp");
See Also
- api-impl-asref - When to use AsRef instead
- api-from-not-into - Why From is preferred
- err-from-impl - From for error conversion