on using borrowed types vs borrowing owned types

The difference that makes all the difference.

2024-11-06

#borrowed types #borrowing #owned #ownership #rust #str #string #vec

The example from Use borrowed types for arguments - Rust Design Patterns:

fn three_vowels(word: &String) -> bool {
    let mut vowel_count = 0;
    for c in word.chars() {
        match c {
            'a' | 'e' | 'i' | 'o' | 'u' => {
                vowel_count += 1;
                if vowel_count >= 3 {
                    return true;
                }
            }
            _ => vowel_count = 0,
        }
    }
    false
}

fn main() {
    let ferris = "Ferris".to_string();
    let curious = "Curious".to_string();
    println!("{}: {}", ferris, three_vowels(&ferris));
    println!("{}: {}", curious, three_vowels(&curious));

    // This works fine, but the following two lines would fail:
    // println!("Ferris: {}", three_vowels("Ferris"));
    // println!("Curious: {}", three_vowels("Curious"));
}

Why doesn’t it work?

As far as the compiler is concerned, these are distinct types.

What are the differences between Rust’s `String` and `str`? - Stack Overflow

But the important point is that using a borrowed type in the function signature allows a type to be coerced, but using a borrowed owned type does not.

From the book:

fn hello(name: &str) {
    println!("Hello, {name}!");
}

let my_box = Box::new(String::from("John")); // my_box: Box<String>
hello(&my_box);

Why does this work? Because Rust implicitly converts the types as many times as it has to until it becomes the type &str.

Another example:

fn do_stuff<T>(v: &Vec<T>) -> &Vec<T> {
	return v;
}

fn better_do_stuff<T>(v: &[T]) -> &[T] {
	return v;
}

let v = &vec![1, 2, 3][0..1];
let u = &vec![1, 2, 3];
let _ = do_stuff(v); // rustc: mismatched types
// expected reference `&Vec<_>`
// found reference `&[{integer}]`
better_do_stuff(v); // is better
better_do_stuff(u); 

When we specify borrowed types in the function signature, we have more flexibility: types that will get coerced will get coerced, but the opposite is not true.