Yet another Rust ownership tutorial
Source: Dev.to
One of the most important concepts to master in Rust is ownership and borrowing. Tons of articles are dedicated to this narrow subject; this tutorial tries to explain the concept with examples.
Ownership is a set of rules that govern how a Rust program manages memory. Rust uses a system of ownership with compile‑time checks; if any rule is violated the program won’t compile, and there is no runtime overhead.
First taste of ownership
Java version
public void own(String text) {}
public static void main(String[] args) {
var text = "my text";
own(text);
System.out.println(text);
}
Rust translation
fn own(_: String) {}
fn main() {
let text: String = String::from("my text"); // 1
own(text);
println!("{}", text);
}
The compiler complains:
error[E0382]: borrow of moved value: `text`
--> src/main.rs:11:20
|
9 | let text: String = String::from("my text");
| ---- move occurs because `text` has type `String`, which does not implement the `Copy` trait
10| own(text);
| ---- value moved here
11| println!("{}", text);
| ^^^^ value borrowed here after move
...
help: consider cloning the value if the performance cost is acceptable
10| own(text.clone());
The own function takes ownership of text, so text cannot be used afterwards.
Clone and Copy
If we follow the compiler’s advice:
fn own(text: String) {}
fn main() {
let text = String::from("my text");
own(text.clone()); // deep copy
println!("{}", text);
}
String implements Clone, which creates a deep copy. This doubles the memory usage for the value.
When a type also implements Copy, the value is duplicated implicitly and we don’t need to call clone():
#[derive(Debug, Copy, Clone)]
struct Dummy {}
fn own(_: Dummy) {}
fn main() {
let dummy = Dummy {};
own(dummy); // copied automatically
println!("{:?}", dummy); // still usable
}
Note: Implementing
Copyfor large structures can be memory‑hungry because every move creates a copy.
Passing by reference
Passing a value moves ownership. To let a function borrow the value, pass a reference with &:
#[derive(Debug)]
struct Dummy {}
fn borrow(_: &Dummy) {}
fn main() {
let dummy = Dummy {};
borrow(&dummy); // borrow, not move
println!("{:?}", dummy); // still usable
}
Reading fields through a reference:
#[derive(Debug)]
struct Dummy {
foo: String,
}
fn borrow(dummy: &Dummy) {
println!("{:?}", dummy.foo);
}
fn main() {
let dummy = Dummy { foo: String::from("Foo") };
borrow(&dummy);
println!("{:?}", dummy); // Dummy { foo: "Foo" }
}
Mutating data
Trying to mutate without mut
#[derive(Debug)]
struct Dummy {
foo: String,
}
fn main() {
let dummy = Dummy { foo: String::from("Foo") };
dummy.foo = String::from("Bar"); // error
}
Compiler error:
error[E0594]: cannot assign to `dummy.foo`, as `dummy` is not declared as mutable
--> src/main.rs:8:5
|
8 | dummy.foo = String::from("Bar");
| ^^^^^^^^^ cannot assign
|
help: consider changing this to be mutable
7 | let mut dummy = Dummy { foo: String::from("Foo") };
Making the variable mutable
#[derive(Debug)]
struct Dummy {
foo: String,
}
fn main() {
let mut dummy = Dummy { foo: String::from("Foo") };
dummy.foo = String::from("Bar");
}
Mutating a parameter by value
#[derive(Debug)]
struct Dummy {
foo: String,
}
fn mutate(mut dummy: Dummy) {
dummy.foo = String::from("Bar");
println!("{:?}", dummy.foo);
}
fn main() {
let dummy = Dummy { foo: String::from("Foo") };
mutate(dummy);
}
Mutating through a mutable reference
#[derive(Debug)]
struct Dummy {
foo: String,
}
fn mutate(dummy: &mut Dummy) {
dummy.foo = String::from("Bar");
println!("Inside: {:?}", dummy.foo);
}
fn main() {
let mut dummy = Dummy { foo: String::from("Foo") };
mutate(&mut dummy);
println!("Outside: {:?}", dummy.foo);
}
Summary
| Goal | How to achieve it |
|---|---|
| Take ownership | Pass by value |
| Duplicate the value | Implement Clone and call clone() |
| Automatically duplicate the value | Implement Copy (implies Clone) |
| Borrow (read‑only) | Pass a reference &T |
| Mutate a local variable | Declare it mut |
| Mutate through a reference | Use &mut T and declare the binding mut |
Originally published at A Java Geek on December 7th, 2025