What are Owned and Borrowed types in Rust? To know these concepts, we need to understand first what Rust Ownership and Rust References.
Get Your Head Around Rust Ownership
When we talk about ownership in Rust, we generally mean – a variable owns a value. As we assign the same value between variables, we move the value, changing the owner. While this is true for Rust Owned types, the behavior is different for Rust References (also known as Borrowed types). We pass around addresses of values somewhere in the memory instead of the values themselves.
Rust Borrowed Types
Rust Borrowed types are types that do not follow the rules for Ownership. Variables of this kind (any Rust data types) have an ampersand (&) symbol in front of them. Consider the following examples.
1 2 3 | let pet_name: &String = &String::from("Gary"); // Type is &String let pet_owner_name: &str = "Karl"; // Type is &str |
What happens when we try to move a reference type between variables? What do you think? Here are some Rust codes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | fn main() { let pet_name: &String = &String::from("Gary"); // Type is &String let pet_owner_name: &str = "Karl"; // Type is &str // Assigning the same value between variables pet_name and pet_alias let pet_alias = pet_name; let pet_owner_alias = pet_owner_name; println!("Pet Name: {}", pet_name); println!("Pet Alias: {}", pet_alias); println!("Pet Owner Name: {}", pet_owner_name); println!("Pet Owner Alias: {}", pet_owner_alias); } |
When we run these codes, we get the following results.
1 2 3 4 | Pet Name: Gary Pet Alias: Gary Pet Owner Name: Karl Pet Owner Alias: Karl |
Did we move any values? Nope!
Rust Owned Types
Now, the Rust Owned types are different from Borrowed types. They follow the Rust Ownership rules. These variables contain the actual values they represent, unlike Borrowed types which have location addresses of the values they represent.
Let us modify the codes from the previous section as follows. Now we are using the String data type for pet_name and pet_owner_name!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | fn main() { let pet_name: String = String::from("Gary"); // Type is String let pet_owner_name: String = String::from("Karl"); // Type is String // Assigning the same value between variables pet_name and pet_alias let pet_alias = pet_name; let pet_owner_alias = pet_owner_name; println!("Pet Name: {}", pet_name); println!("Pet Alias: {}", pet_alias); println!("Pet Owner Name: {}", pet_owner_name); println!("Pet Owner Alias: {}", pet_owner_alias); } |
When we run these codes, we will get the following compilation errors.
1 2 3 4 5 6 7 8 9 10 11 12 13 | error[E0382]: borrow of moved value: `pet_name` --> src\main.rs:9:30 | 2 | let pet_name: String = String::from("Gary"); // Type is String | -------- move occurs because `pet_name` has type `String`, which does not implement the `Copy` trait ... 6 | let pet_alias = pet_name; | -------- value moved here ... 9 | println!("Pet Name: {}", pet_name); | ^^^^^^^^ value borrowed here after move | = note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info) |
The error messages could confuse newbies who lack the basic understanding of Rust Owned and Borrowed types, even Rust Ownership rules. Notice the message error uses the root words “borrow” and “move.”
Okay, so what’s next? With a better understanding of Rust Owned and Borrowed types, we could work with Rust more confidently. For example, now we know that we could return Owned values from functions and methods without resorting to global static variables. In the same token, we now know that we should not return a reference type from a function or method.
Are you having fun so far? There are really Owned and Borrowed types, and we didn’t make them up! Check the following codes.
1 2 3 4 5 6 7 8 9 10 | fn main() { let pet_name: String = String::from("Gary"); // Type is String let pet_owner_name: String = String::from("Karl"); // Type is String let borrowed_type_variable: &str = "this is a string"; let owned_type_variable:String = borrowed_type_variable.to_owned(); println!("borrowed - {}", borrowed_type_variable); println!("owned - {}", owned_type_variable); } |
Notice the call to the to_owned() method? There you go!