Course contentsShow
← Rust
Lesson 244 of 1,9103. Borrowing and ReferencesFree lesson

Borrowing vs Owning in Generics

Write generic code that remains flexible by accepting either owned or borrowed data.

In this lesson, you will learn how to write flexible generic functions that can accept both owned data and borrowed references without forcing the caller to choose one or the other.

The Problem: Strict Constraints

When you write a generic function, you might be tempted to restrict your input types. For example, if you write fn process<T: Display>(data: T), the function must take ownership of T. If the caller has a reference, they have to dereference it (if possible), or if they have an owned object they want to keep, they are forced to clone it. This isn't always efficient or convenient.

The Solution: Using AsRef

Instead of demanding ownership, we can use the AsRef trait. This trait allows a type to be viewed as a reference to another type. By constraining our generic T with AsRef<str>, for example, we tell Rust: "I don't care if you give me a String (owned) or a &str (borrowed), as long as I can look at it as a string slice."

fn print_data<T: AsRef<str>>(input: T) {
    // We convert whatever we got into a &str
    let view = input.as_ref();
    println!("Data: {}", view);
}

fn main() {
    let owned = String::from("Hello");
    let borrowed = "World";

    print_data(owned);    // Works: String implements AsRef<str>
    print_data(borrowed); // Works: &str implements AsRef<str>
}

Why This Matters

By using AsRef, your API becomes significantly more flexible. You aren't forcing the user to give up ownership of their variable, nor are you forcing them to perform an expensive .clone() just to satisfy a function signature. The function logic receives exactly what it needs—a reference—regardless of how the caller holds the data.

This approach follows the Rust philosophy of keeping data flow flexible: if you only need to read or view data, use a trait bound that expresses that requirement rather than demanding the data itself.

Key Takeaway: Use trait bounds like AsRef<T> in your generic functions to accept both owned values and borrowed references, keeping your API ergonomic and efficient.