Course contentsShow
← Rust
Lesson 598 of 1,9105. Type System and TraitsFree lesson

Implementing traits for non-copyable types

Manage resource ownership while satisfying generic trait interfaces.

In this lesson, you will learn how to design trait implementations for types that own unique resources, ensuring your code remains safe without relying on implicit cloning.

The Challenge of Non-Copyable Types

In Rust, many types are "non-copyable" because they manage heap memory, file handles, or network sockets. When you implement a trait for these types, you cannot simply copy the data to satisfy the interface. You must respect the move semantics—meaning that if your trait method signature takes self (the value itself), you are consuming the resource.

Managing Ownership in Trait Methods

When defining a trait, you have three primary choices for how to handle self. Your choice determines whether your implementation can support non-copyable types effectively:

  1. &self: The method borrows the instance. This is ideal for non-copyable types because it keeps the resource alive.
  2. &mut self: The method borrows the instance mutably. This allows you to modify the resource without taking ownership.
  3. self: The method takes ownership. This destroys the non-copyable instance after the method executes.

Consider a Lock struct that manages a system resource:

struct Lock {
    id: String, // String is not Copy
}

trait Security {
    fn verify(&self) -> bool;
    fn consume(self); // Takes ownership
}

impl Security for Lock {
    fn verify(&self) -> bool {
        !self.id.is_empty()
    }

    fn consume(self) {
        println!("Destroying lock: {}", self.id);
        // The lock is dropped here as it goes out of scope
    }
}

Strategic Implementation

If your trait requires an interface where the implementer must return a clone, but your type is non-copyable, you cannot derive Clone. Instead, you must explicitly implement Clone for your type first, or design your trait methods to return a new instance using &self.

When working with non-copyable types, always prefer using references (&self) in your trait signatures unless the specific logic requires the total destruction of the underlying resource. This ensures your code remains flexible enough to handle complex, resource-heavy types without forcing unnecessary data duplication.

Key Takeaway: When implementing traits for non-copyable types, favor &self or &mut self to preserve ownership, and only use self when the trait logic intentionally requires consuming the resource.