0% found this document useful (0 votes)
2 views

COMP3007 Modern Programming Languages-Week 4

The document outlines the concepts of ownership and borrowing in Rust, highlighting its memory management features that avoid common bugs like dangling pointers and memory leaks. It explains the rules of ownership, the differences between stack and heap memory, and how Rust's system ensures memory safety without garbage collection. Additionally, it covers move semantics, cloning, and the behavior of basic types in Rust, emphasizing efficient memory usage and automatic deallocation.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views

COMP3007 Modern Programming Languages-Week 4

The document outlines the concepts of ownership and borrowing in Rust, highlighting its memory management features that avoid common bugs like dangling pointers and memory leaks. It explains the rules of ownership, the differences between stack and heap memory, and how Rust's system ensures memory safety without garbage collection. Additionally, it covers move semantics, cloning, and the behavior of basic types in Rust, emphasizing efficient memory usage and automatic deallocation.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 40

COMP3007 - Modern Programming Languages

Week 4: Rust - Ownership and Borrowing

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel

Department of Computer Engineering

Fall 2024-2025

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 1 / 40
Outline
1 Introduction to Ownership
2 What is Ownership?
3 Common Memory Safety Issues
4 Stack and Heap
5 Variable Scope
6 The String Type
7 Move Semantics
8 Clone
9 Ownership and Functions
10 Return Values and Scope
11 References and Borrowing
12 The Rules of References
13 The Slice Type
14 Conclusion
Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department
COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 2 / 40
What is Ownership?

Core feature of Rust


Memory management without garbage collection
Set of rules the compiler checks at compile time
Ensures memory safety and prevents common bugs

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 3 / 40
Ownership Rules

1 Each value in Rust has a variable that’s called its owner.


2 There can only be one owner at a time.
3 When the owner goes out of scope, the value will be dropped.

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 4 / 40
What is Ownership? (Expanded)

A central feature that sets Rust apart from other languages


A system for managing memory through a set of rules checked at
compile-time
Enables Rust to make memory safety guarantees without needing a
garbage collector
Addresses common programming problems like:
Dangling pointers
Double free errors
Memory leaks
Allows efficient runtime performance

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 5 / 40
Common Memory Safety Issues

Rust’s ownership system prevents several common memory-related


bugs:
Dangling pointers
Double-free errors
Memory leaks
Understanding these issues helps appreciate Rust’s safety guarantees

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 6 / 40
Dangling Pointers

A dangling pointer refers to memory that has been freed or is no


longer valid
Can lead to undefined behavior or security vulnerabilities
1 fn main () {
2 let reference_to_nothing = dangle ();
3 }
4
5 fn dangle () -> & String { // RUST COMPILER
ERROR
6 let s = String :: from(" hello ");
7 &s // We return a reference to s
8 } // s goes out of scope and is dropped . Its memory is
freed .

Rust prevents this at compile-time

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 7 / 40
Double-free Errors

Occurs when a program tries to free memory that has already been
freed
Can cause memory corruption, crashes, or security vulnerabilities
1 fn main () {
2 let s1 = String :: from(" hello ");
3 let s2 = s1; // s1 is moved to s2
4
5 println !("{}, world !", s1); // RUST COMPILER ERROR
6 // Rust prevents use of s1 after move
7 }

Rust’s ownership rules prevent double-free errors

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 8 / 40
Memory Leaks

Occurs when allocated memory is never freed


Can lead to excessive memory usage and program slowdown
Rust mostly prevents memory leaks, but they’re still possible:
1 use std::rc::Rc;
2 use std:: cell :: RefCell;
3
4 #[ derive(Debug)]
5 struct Node {
6 next: Option <Rc <RefCell <Node >>>,
7 }
8
9 fn main () {
10 let a = Rc::new(RefCell ::new(Node { next: None }));
11 let b = Rc::new(RefCell ::new(Node { next: Some(a.clone ()) }));
12
13 // Create a reference cycle
14 a.borrow_mut ().next = Some(b.clone ());
15
16 // a and b now refer to each other , creating a memory leak
17 }

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 9 / 40
How Rust Prevents Memory Issues

Ownership Rules:
Each value has a single owner
When the owner goes out of scope, the value is dropped
Borrowing Rules:
Multiple immutable references OR one mutable reference
References must always be valid
Lifetime Annotations:
Ensure references are valid for the duration they’re used
Move Semantics:
Prevent use after move, eliminating double-free errors

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 10 / 40
Stack vs Heap: Overview

Heap
Stack
Flexible memory allocation
Fast memory allocation
Dynamic size, unknown at
LIFO (Last In, First Out)
compile time
Fixed size, known at compile
Slower allocation and access
time
Manual memory management
Automatic memory management
(in many languages)

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 11 / 40
Stack and Heap

Heap
x: 5 String data
y: 10
Vec<i32> data
Stack

z: 15
ptr: 0x..
Free space
...

Stack: Fixed-size data, fast access. Heap: Dynamic-size data, flexible but slower.

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 12 / 40
Memory Usage in Rust

Rust uses both stack and heap efficiently


Stack:
Stores values with known, fixed size
e.g., integers, floats, boolean, char, tuples of fixed-size types
Heap:
Stores values with unknown size at compile time or size that might
change
e.g., String, Vec, Box
Ownership system manages heap allocations and deallocations
Prevents common memory-related bugs at compile time

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 13 / 40
Variable Scope

1 { // s is not valid here , it 's not


yet declared
2 let s = " hello "; // s is valid from this point
forward
3 // do stuff with s
4 } // this scope is now over , and s
is no longer valid

A scope is the range within a program for which an item is valid


When s comes into scope, it is valid
It remains valid until it goes out of scope

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 14 / 40
The String Type

1 let s = String :: from(" hello ");

String type is allocated on the heap


Can be mutated, unlike string literals
Demonstrates ownership concepts clearly

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 15 / 40
The String Type: Memory and Allocation

1 let mut s = String :: from("hello");


2 s.push_str(", world!"); // push_str () appends a literal to a String
3 println !("{s}"); // This will print `hello , world!`

String type is allocated on the heap


Can be mutated, unlike string literals
Demonstrates ownership concepts clearly

Memory is requested from the allocator at runtime


Memory is returned to the allocator when the String is dropped
The ‘mut‘ keyword allows the String to be modified
‘push_str()‘ method appends a string slice to the String

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 16 / 40
String Memory Layout

A String consists of three parts:


s
Pointer to the memory on the
heap ptr index value
Length (how much memory is 0 h

Stack
e

Heap
1
currently in use) len: 5 2 l
Capacity (total amount of 3 l
capacity: 5 4 o
memory received from
allocator)

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 17 / 40
Scope and Automatic Deallocation

1 {
2 let s = String :: from(" hello "); // s is valid from
this point forward
3
4 // do stuff with s
5 } // this scope is now
over , and s is no
6 // longer valid

Rust automatically returns memory to the allocator when a variable


goes out of scope
At the closing curly brace, Rust calls a special function called ‘drop‘
‘drop‘ is where the author of ‘String‘ can put the code to return the
memory
Rust calls ‘drop‘ automatically at the closing curly brace

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 18 / 40
Memory Deallocation in Rust

Unlike languages with a garbage collector, Rust frees memory as soon


as it’s no longer needed
No need to explicitly free memory
No risk of forgetting to free memory (avoiding memory leaks)
No risk of freeing memory twice (avoiding double free errors)
Memory is freed exactly when the variable goes out of scope
This pattern has a profound impact on how Rust code is written

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 19 / 40
Move Semantics in Rust
Without Move Semantics
With Move Semantics s1
s1 ptr index value
ptr len: 5 0 h

Stack

Heap
index value 1 e

len: 5 0 h capacity: 5 2 l
Stack

Heap
1 e 3 l
4 o
capacity: 5 2
3
l
l s2
4 o
s2 ptr index value
0 h

Heap
ptr 1
2
e
l
3 l
4 o

With Move Semantics:


s2 takes ownership of the data
s1 becomes invalid (shadowed), preventing double free
Efficient: No data copying
Without Move Semantics:
Data would be copied, doubling memory usage
Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department
COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 20 / 40
Clone

1 let s1 = String :: from(" hello ");


2 let s2 = s1.clone ();
3 println !("s1 = {}, s2 = {}", s1 , s2);

clone creates a deep copy of the heap data


Both s1 and s2 are valid
More expensive than moving

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 21 / 40
Behavior of Basic Types in Rust

Basic types (e.g., integers, floats,


booleans):
Have known size at compile time x
Stored on the stack
Implement the Copy trait 5

Stack
Copy
Assignment creates a copy, not a move y
Original variable remains valid after
assignment 5
No need for explicit cloning
More efficient for small, fixed-size data

1 let x = 5;
2 let y = x; // x is copied , not moved
3 println !("x is still accessible: {}", x); // This is valid

Unlike String and other heapallocated types, assigning a basic type doesn’t transfer ownership or invalidate the original variable.

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 22 / 40
Ownership and Functions

1 fn main () {
2 let s = String :: from("hello"); // s comes into scope
3 takes_ownership(s); // s's value moves into the function ...
4 // ... and so is no longer valid here
5 let x = 5; // x comes into scope
6 makes_copy(x); // x would move into the function ,
7 // but i32 is Copy , so it 's okay to still
8 // use x afterward
9 } // Here , x goes out of scope , then s. But because s's value was moved , nothing
10 // special happens.
11
12 fn takes_ownership(some_string: String) { // some_string comes into scope
13 println !("{}", some_string);
14 } // Here , some_string goes out of scope and `drop ` is called. The backing
15 // memory is freed.
16
17 fn makes_copy(some_integer: i32) { // some_integer comes into scope
18 println !("{}", some_integer);
19 } // Here , some_integer goes out of scope. Nothing special happens.

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 23 / 40
Return Values and Scope

1 fn main () {
2 let s1 = gives_ownership (); // gives_ownership moves its return
3 // value into s1
4 let s2 = String :: from("hello"); // s2 comes into scope
5 let s3 = takes_and_gives_back(s2); // s2 is moved into
6 // takes_and_gives_back , which also
7 // moves its return value into s3
8 } // Here , s3 goes out of scope and is dropped. s2 was moved , so nothing
9 // happens. s1 goes out of scope and is dropped.
10
11 fn gives_ownership () -> String {
12 let some_string = String :: from("yours"); // some_string comes into scope
13 some_string // some_string is returned and
14 // moves out to the calling function
15 }
16
17 fn takes_and_gives_back(a_string: String) -> String { // a_string comes into scope
18 a_string // a_string is returned and moves out to the calling function
19 }

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 24 / 40
References and Borrowing

1 fn main () {
2 let s1 = String :: from(" hello ");
3 let len = calculate_length (&s1);
4 println !("The length of '{}' is {}.", s1 , len);
5 }
6
7 fn calculate_length (s: & String ) -> usize {
8 s.len ()
9 }

References allow you to refer to some value without taking ownership


of it
& indicates that we’re passing a reference
This is called ”borrowing”

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 25 / 40
References in Rust

A reference is like a pointer: an address we


can follow to access data
Unlike pointers, references are guaranteed
to point to valid data
References allow using a value without
taking ownership
s s1

Created using the & symbol name value


ptr
name value
ptr
index value
0 h

Opposite of referencing (&) is dereferencing


len 5 1 e
capacity 5 2 l

(*) 3
4
l
o

1 fn main () {
2 let s1 = String :: from("hello");
3 let len = calculate_length (&s1);
4 println !("The length of '{s1}' is {len}.");
5 }
6
7 fn calculate_length(s: &String) -> usize {
8 s.len()
9 }

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 26 / 40
Attempting to Modify a Borrowed Value

Key Points:
References are immutable
by default
1
2
fn main () {
let s = String :: from("hello"); Attempting to modify a
3
4 change (&s);
borrowed value results in a
5 } compiler error
6
7
8
fn change(some_string: &String) {
some_string.push_str(", world"); This prevents data races
9 } and ensures memory safety
To modify a borrowed
value, we need to use a
mutable reference (&mut)
Rust’s borrowing rules prevent unexpected modifications to data, enhancing
program safety and predictability.

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 27 / 40
Mutable References

1 fn main () {
2 let mut s = String :: from(" hello ");
3 change (& mut s);
4 }
5

6 fn change ( some_string : &mut String ) {


7 some_string . push_str (", world ");
8 }

Mutable references allow you to modify a borrowed value


Only one mutable reference to a particular piece of data in a
particular scope

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 28 / 40
The Rules of References

At any given time, you can have either (but not both):
One mutable reference
Any number of immutable references
References must always be valid (no dangling references)

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 29 / 40
Mutable References in Rust

Examples:
Not allowed (two mutable refs):
Corrected code using mutable reference:
1 let mut s = String :: from("hello");
1 fn main () { 2 let r1 = &mut s;
2 let mut s = String :: from("hello"); 3 let r2 = &mut s; // Error!
3 change (& mut s);
4 }
5 Not allowed (mutable and immutable):
6 fn change(some_string: &mut String) {
7 some_string.push_str(", world"); 1 let mut s = String :: from("hello");
8 } 2 let r1 = &s;
3 let r2 = &mut s; // Error!

Key Rules: Allowed (non-overlapping scopes):


Only one mutable reference to a particular piece of data
in a particular scope. 1 let mut s = String :: from("hello");
2 {
Can’t have a mutable reference while having an
3 let r1 = &mut s;
immutable one to the same value.
4 } // r1 goes out of scope
Multiple immutable references are allowed. 5 let r2 = &mut s; // OK

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 30 / 40
Compiler Outputs for Mutable Reference Errors
Multiple mutable references error:
$ cargo run
Compiling ownership v0.1.0 (file:///projects/ownership)
error[E0499]: cannot borrow `s` as mutable more than once at a time
--> src/main.rs:5:14
|
4 | let r1 = &mut s;
| ------ first mutable borrow occurs here
5 | let r2 = &mut s;
| ^^^^^^ second mutable borrow occurs here
6 |
7 | println!("{}, {}", r1, r2);
| -- first borrow later used here

For more information about this error, try `rustc --explain E0499`.
error: could not compile `ownership` due to previous error

Mutable borrow with immutable references error:


$ cargo run
Compiling ownership v0.1.0 (file:///projects/ownership)
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
--> src/main.rs:6:14
|
4 | let r1 = &s; // no problem
| -- immutable borrow occurs here
5 | let r2 = &s; // no problem
6 | let r3 = &mut s; // BIG PROBLEM
| ^^^^^^ mutable borrow occurs here
7 |
8 | println!("{}, {}, and {}", r1, r2, r3);
| -- immutable borrow later used here
Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department
COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 31 / 40
The Slice Type

1 let s = String :: from(" hello world ");


2 let hello = &s [0..5];
3 let world = &s [6..11];

Slices let you reference a contiguous sequence of elements in a


collection
They don’t take ownership of the data

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 32 / 40
The Slice Type in Rust

Slices let you reference a contiguous sequence of elements in a


collection
Slices don’t take ownership of the underlying data
Common slice type: string slice (&str)

Example of creating a string slice:


1 let s = String :: from(" hello world ");
2 let hello = &s [0..5];
3 let world = &s [6..11];

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 33 / 40
String Slices in Detail

s
A string slice is a reference to part of a name value index value
String
ptr 0 h
len 11 1 e
Syntax: capacity 11 2 l
[starting_index..ending_index] 3 l

starting_index: first position world 4 o


5
(inclusive)
name value
ptr 6 w
ending_index: one more than the last len 5 7 o
position 8 r
9 l
Internally: stores the starting position 10 d
and the length of the slice

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 34 / 40
String Slice Range Syntax

1 let s = String :: from(" hello ");


2
3 let slice = &s [0..2];
4 let slice = &s [..2]; // same as above
5
6 let len = s.len ();
7
8 let slice = &s[3.. len ];
9 let slice = &s [3..]; // same as above
10
11 let slice = &s[0.. len ];
12 let slice = &s[..]; // same as above

If starting at index 0, you can drop the first number


If including the last byte, you can drop the trailing number
You can drop both values to take a slice of the entire string

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 35 / 40
String Literals as Slices

String literals are actually slices


Type of s here is &str:
1 let s = "Hello , world !";

&str is an immutable reference


This explains why string literals are immutable

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 36 / 40
String Slices as Parameters

1 fn first_word (s: &str) -> &str {


2 let bytes = s. as_bytes ();
3
4 for (i, &item) in bytes.iter (). enumerate () {
5 if item == b' ' {
6 return &s[0..i];
7 }
8 }
9
10 &s[..]
11 }

Using &str allows the function to accept both &String and &str
More flexible and doesn’t take ownership

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 37 / 40
Other Slice Types

Slices aren’t specific to strings


Array slices:
1 let a = [1, 2, 3, 4, 5];
2 let slice = &a [1..3];
3 assert_eq !( slice , &[2, 3]);

This slice has the type &[i32]


Works the same way as string slices
Stores a reference to the first element and a length

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 38 / 40
Conclusion

Ownership is Rust’s most unique feature


It enables Rust to make memory safety guarantees without needing a
garbage collector
Understanding ownership, borrowing, and slices is crucial for writing
efficient Rust programs
These concepts may take time to fully grasp - practice is key!

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 39 / 40
Thank You! Any Questions?

Dr. Öğr. Üyesi Yusuf Kürşat Tuncel (Department


COMP3007
of Computer
- Modern
Engineering)
Programming Languages Fall 2024-2025 40 / 40

You might also like