Ownership, Borrowing, and Clone Policy
Rule
Borrow inputs that are only read, take ownership of values stored unchanged, keep stored data owned, and clone when it keeps APIs simpler at acceptable cost.
Why
Borrowed read-only inputs accept both owned and borrowed callers, while owned storage and results keep lifetimes from spreading through APIs.
Do
- Accept
&str,&[T], and&Pathfor read-only inputs. - Take owned values or
impl Into<T>when a constructor or setter stores them unchanged. - Borrow values that are validated or transformed, then allocate the derived representation.
- Store
String,Vec<T>, andPathBufrather than borrowed fields by default. - Borrow from plain accessors and use ownership-signaling names for snapshots or clones.
- Return owned data when a borrow would expose unnecessary lifetimes.
- Use
.clone()consistently for ordinary values,Rc, andArc. - Use
IntoIterator,AsRef,Into, orCowonly when the flexibility solves a real caller problem and remains local. - Revisit clone cost only with profiling or domain evidence.
Avoid
- Do not take owned containers when a function only reads them.
- Do not accept
&String,&Vec<T>, or&PathBufby habit. - Do not store references merely to avoid a cheap allocation.
- Do not hide clones in bare-noun accessors.
- Do not add generic conversion bounds to every function.
- Do not use
Cowto avoid making an ownership decision. - Do not mix
Arc::clone(&value)andvalue.clone()conventions.
Public API Notes
Concrete borrowed refs are usually clearer than generic bounds. Document large or surprising clones in published APIs.
Example
#![allow(unused)]
fn main() {
pub struct Settings {
service_name: String,
root: PathBuf,
}
impl Settings {
pub fn new(service_name: impl Into<String>, root: PathBuf) -> Self {
Self {
service_name: service_name.into(),
root,
}
}
pub fn service_name(&self) -> &str {
&self.service_name
}
pub fn root(&self) -> &Path {
&self.root
}
}
}
Exceptions
- Use
impl AsRef<Path>when accepting path-like callers is the main ergonomic benefit. - Use
impl Readorimpl Writefor reusable data-processing seams. - Use
Cowfor local parsing or normalization that usually borrows but sometimes allocates. - Avoid clones in measured hot paths or for resource-heavy types.