Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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 &Path for 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>, and PathBuf rather 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, and Arc.
  • Use IntoIterator, AsRef, Into, or Cow only 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 &PathBuf by 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 Cow to avoid making an ownership decision.
  • Do not mix Arc::clone(&value) and value.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 Read or impl Write for reusable data-processing seams.
  • Use Cow for local parsing or normalization that usually borrows but sometimes allocates.
  • Avoid clones in measured hot paths or for resource-heavy types.