# Referenser & pekare i Rust jämfört med C++

28 augusti 2026 | https://www.ribomation.se/blog/2026/referenser-and-pekare-i-rust-jamfort-med-c++/

Om du frågar en C++-programmerare vad en referens är blir svaret ofta "ett alias", och en pekare "en adress med en typ på". Rimlig gissning för Rust också — samma tecken, `&` och `*`, samma grundidé. Men gissningen håller bara i några rader kod: en Rust-referens går att peka om till ett helt annat värde, något som är omöjligt i C++, och kompilatorn håller reda på exakt hur länge varje lån får leva. Och `unsafe` visar sig inte vara någon genväg runt reglerna, utan en dörr rakt in i samma minnesbuggar som C++ alltid haft — use-after-free, oinitierat minne, allt fortfarande fullt möjligt, bara att man måste be om det uttryckligen.

Den här artikeln går igenom skillnaderna ett steg i taget: modifieringsbarhet, referenser och deras låneregler, råa pekare bakom `unsafe`, och till sist tre sätt att allokera på heapen i Rust, med C++ som jämförelsepunkt hela vägen.

* * *

Variabler i Rust
----------------

En variabel deklareras i Rust med satsen

```rust
let var: type = init
```

Typen kan vanligtvis utelämnas eftersom kompilatorn kan klura ut detta själv. Till skillnad mot de flesta andra programmeringsspråk är variabeln konstant, dvs icke modifieringsbar, eller på engelska _immutable_.

Om man däremot vill deklarera en variabel som kan modifieras, trycker man in `mut` efter `let`. Därmed blir den _mutable_.

```rust
let value1 = 42;
value1 += 1; //error[E0384]: cannot assign twice to immutable variable `value1`

let mut value2 = 42;
value2 += 1; 
```

I C++ är det ju tvärtom, man trycker in const för att få en variabel immutable.

```cpp
int value1 = 42;
value1++;

int const value2 = 37; // "const int" works too
value2++; //error: increment of read-only variable ‘value2’
```

* * *

Referenser i Rust
-----------------

Referenser finns i både Rust och C++, men skiljer sig åt på flera punkter, som vi kommer att se. Att deklarera en referens i Rust görs med satsen.

```rust
let var: &type = &target
```

Precis som för en vanlig variabel kan referens-typen utelämnas, eftersom kompilatorn kan från uttrycket `&target` lista ut att vi har en referens till ett värde av den typ `target` har.

### Immutable refs

Notera att Rust använder och-tecknet (`&`) för att denotera en referens, vilket ju i C och C++ är adressoperatorn. I Rust är allting explicit; typer, operatorer med mera. Tja, i alla fall nästan - det finns förenklingar/genvägar. Man kommer åt värdet på en referens i Rust via stjärna (`*`) vilket ju är dereferens-operatorn också i C/C++.

```rust
let target: i32 = 42;
let refvar: &i32 = &target;
println!("target={}, *ref={}", target, *refvar);
println!("target={}, ref={}", target, refvar); //auto-deref
```

```shell
target=42, *ref=42
target=42, ref=42
```

### Mutable refs

Här ovan har vi enbart variabler som är immutable. Önskar man muterbarhet, behöver man "gödsla" med operatorn `mut`. I exemplet nedan har vi en mutable referens som ändrar värde på målvariabeln.

```rust
let mut value = 42;
print!("a) value={}", value);
let rf = &mut value;
println!(", ref={}", rf);

*rf = 37;
print!("b) ref={}", rf);
println!(", value={}", value);
```

```shell
a) value=42, ref=42
b) ref=37, value=37
```

En icke obetydlig semantisk skillnad mellan referenser i Rust respektive C++ är att i Rust är det en vanlig variabel med en plats och en storlek, medan i C++ är det ett alias som hanteras av kompilatorn. OK, bakom kulisserna i C++ så realiseras det som pekare, men det är ingenting vi kan använda oss av. I Rust fungerar referenser mer som pekare i C/C++, men med ganska tydliga semantiska skillnader.

### Owning vs. Borrow

Äga och låna är två tydliga koncept i Rust. Varje värde har exakt en ägare, vilken ansvarar för att frigöra eventuella resurser i samband med att värdet faller ur sitt giltighetsområde (_scope_). Ägarskapet kan överföras till någon annan via tilldelning eller parameteröverföring. Detta sker implicit i Rust, medan det är explicit i C++ via `std::move` (tja, implicit i vissa fall med s.k. _xvalue_).

Det går också att låna ett värde (_borrow_) i Rust och det är för detta ändamål vi har referenser. Hur detta hanteras bestäms av följande regler:

1.  Ett värde kan lånas immutable ett godtyckligt antal gånger samtidigt.
2.  Ett värde kan lånas mutable exakt en gång i taget.
3.  Lånetyperna kan aldrig blandas - strikt antingen eller.
4.  Livslängden för ett lån får aldrig överstiga livslängden för värdet som lånas ut.

Ett lån varar tills referensvariabeln faller ur sitt giltighetsområde, eller, som vi ska se strax, tills lånet inte längre behövs. OK, här kommer ett belysande exempel:

```rust
let mut value: i32 = 42;
value += 1; //this is allowed

let rf: &i32 = &value;
println!("a) value={}, ref={}", value, *rf);

//rf += 2;
//error[E0368]: binary assignment operation `+=` cannot be applied to type `&i32`

//value += 3; //this is now forbidden
//error[E0506]: cannot assign to `value` because it is borrowed
println!("b) value={}, ref={}", value, *rf);
//                                     --- borrow later used here

print!("c) ref={}", *rf); //the borrow stops here
value += 4; //now, it's allowed again
println!(", value={}", value);
```

```shell
a) value=43, ref=43
b) value=43, ref=43
c) ref=43, value=47
```

### Re-assignment

Precis som med pekare —rent allmänt— så kan referenser i Rust omtilldelas (_re-assignment_).

```rust
let mut first = 42;
print!("a) first={}", first);
let mut rf = &mut first;
println!(", ref={}", rf);

*rf = 37; //assign value to target
print!("b) ref={}", rf);
println!(", first={}", first);

let mut second = 17;
rf = &mut second; //assign new target
print!("c) ref={}", rf);
println!(", first={}", first);
```

```shell
a) first=42, ref=42
b) ref=37, first=37
c) ref=17, first=37
```

Detta är omöjligt i C++, utan en tilldelning propagerar till målvariabeln.

```cpp
int first = 42;
int& ref = first;
std::println("a) first={}, ref={}", first, ref);

ref = 37; //assign value to target
std::println("b) first={}, ref={}", first, ref);

int second = 37;
ref = second; //also, assign value to target
std::println("c) ref={}", ref);
std::println("c) 1st={}, 2nd={}", first, second);
```

```shell
a) first=42, ref=42
b) first=37, ref=37
c) ref=37
c) 1st=37, 2nd=37
```

### Composite Values

Hur fungerar det då med sammansatta värden, såsom en struct? Tja, på liknande sätt. Läs kodkommentarerna nedan noga, för att följa lånens livslängder.

```rust
#[derive(Debug)]
struct Point { x: f32, y: f32 }
//...
let mut pnt = Point { x: 5.0, y: 10.0 };
let rf: &Point = &pnt;
println!("a) value={:?}, ref={:?}", pnt, *rf);

//rf.x *= 2.5;
//^^^^^^^^^ `rf` is a `&` reference, so it cannot be written to
//error[E0594]: cannot assign to `rf.x`, which is behind a `&` reference

//pnt.x *= 2.71; //error[E0506]: cannot assign to `pnt.x` because it is borrowed
println!("b) value={:?}, ref={:?}", pnt, *rf);
//                                       -- borrow later used here

println!("c) ref={:?}", *rf); //the borrow stops here
pnt.x *= 2.71;
pnt.y *= 3.14;
println!("c) value={:?}", pnt);
```

```shell
a) value=Point { x: 5.0, y: 10.0 }, ref=Point { x: 5.0, y: 10.0 }
b) value=Point { x: 5.0, y: 10.0 }, ref=Point { x: 5.0, y: 10.0 }
c) ref=Point { x: 5.0, y: 10.0 }
c) value=Point { x: 13.55, y: 31.400002 }
```

* * *

Pekare i Rust
-------------

Även om Rust är designat på så vis att referenser är att föredra i det flesta fall, finns det också pekare i den mer klassiska bemärkelsen. Pekare i Rust fungerar på ett sätt som har en hel del likheter, men också skillnader, jämfört med C/C++.

Precis som för övriga typer av variabler i Rust måste man skilja mellan immutable respektive mutable. Syntaxen har _vissa_ likheter med C/C++. Vill man ha den förstnämnda, används `*const` och för den sistnämnda `*mut`.

```rust
let value1 = 42;
let ptr1: *const i32 = &value1; //immutable ptr to immutable value

let mut value2 = 37;
let ptr2: *mut i32 = &mut value2; //mutable ptr to mutable value
```

Huvudargumentet för Rust är ju _safety_, vilket vi redan sett exempel på i min artikelserie. Men Rust är också ett pragmatiskt språk (precis som C++) så det finns ett sätt att temporärt stiga ur säkerhetsburen genom att utföra vissa operationer i ett `unsafe` block.

Så för pekare i Rust gäller att man kan fritt deklarera dessa, men vill man komma åt det de pekar på, måste detta göras inom ett unsafe-block.

Så här gör man med en immutable pekare.

```rust
let value = 42;
let ptr: *const i32 = &value;
unsafe {
    println!("val={}, ptr={}", value, *ptr);
}
```

```shell
val=42, ptr=42
```

Och så här med en mutable dito.

```rust
let mut value = 42;
let ptr: *mut i32 = &mut value;
unsafe {
    println!("a) val={}, ptr={}", value, *ptr);
    *ptr *= 10;
    println!("b) val={}, ptr={}", value, *ptr);
}
value += 12;
unsafe {
    println!("c) val={}, ptr={}", value, *ptr);
}
```

```shell
a) val=42, ptr=42
b) val=420, ptr=420
c) val=432, ptr=432
```

* * *

Heap-allokerade objekt
----------------------

Pekare till stack-allokerade värden har ett begränsat användningsområde, men det är givetvis när man har heap-allokerade minnesblock/objekt som pekare är som mest användbara. Hur fungerar detta då i Rust, givet de begränsningar vi sett ovan?

### Box

Om vi börjar med den rekommenderade metoden i Rust, är det att använda smarta pekare. Bibliotekstypen `Box` motsvarar `std::unique_ptr` i C++, men med en distinkt skillnad: `Box::new` allokerar alltid sitt minnesblock som en del av konstruktionen. `unique_ptr` kan även ta över ägarskapet av en redan allokerad rå-pekare (t.ex. `unique_ptr<int> p(new int(42))`) - något Box inte har en motsvarighet till. I övrigt fungerar de på samma sätt, när ett Box-objekt lämnar sitt giltighetsområde utförs dess `drop`\-funktion (motsvarar _destructor_) vilken frigör minnesblocket. Med andra ord, RAII används också (flitigt) i Rust, precis som i C++. Skillnaden är syntaktisk: _constructor_ vs. `Type::new()` respektive _destructor_ vs. `drop()`.

Så här kan det se ut:

```rust
fn scalar() {
    println!("--- scalar ---");
    let mut ptr: Box<i32> = Box::new(42);
    println!("a) ptr={}", *ptr);

    *ptr *= 10; //must deref explicit here
    println!("b) ptr={}", ptr); //auto-deref
} //ptr goes out of scope here, and it will drop the memory block


#[derive(Debug)] struct Point { x: f32, y: f32 }

fn composite() {
    println!("--- composite ---");
    let mut ptr = Box::new(Point {x: 5.5, y: 42.0});
    println!("a) ptr={:?}", *ptr);

    (*ptr).x *= 10.0; //explicit deref
    ptr.y *= 10.0;    //auto-deref
    println!("b) ptr={:?}", *ptr);
}
```

```shell
--- scalar ---
a) ptr=42
b) ptr=420
--- composite ---
a) ptr=Point { x: 5.5, y: 42.0 }
b) ptr=Point { x: 55.0, y: 420.0 }
```

Det finns ytterligare några smarta pekartyper i Rust, såsom `Rc<T>` och `Arc<T>`, men dessa får vänta till en annan artikel.

Så Box är en typ i standardbiblioteket, vilket innebär att Box är implementerad i Rust, vilket i sin tur innebär att på något sätt så har ju metoden `new` allokerat ett minnesblock och typens `drop` implementation innehåller Rust-kod för att frigöra densamma. Så helt klart går det att allokera minnesblock i Rust. Detta ska vi kika närmare på nu, via två olika vägar.

### `std::alloc`

Först, via `std::alloc` vars funktioner är _unsafe_ och därför måste hanteras inom ett `unsafe`\-block. Här kommer ett exempel med en skalär.

```rust
use std::alloc::{Layout, alloc, dealloc};
use std::ptr::null_mut;
//. . .
let layout = Layout::new::<i32>();
unsafe {
    let mut ptr: *mut i32 = alloc(layout) as *mut i32;
    println!("a) ptr={} @ {}", *ptr, ptr as usize); //oops, UB both in Rust and C++

    *ptr = 42; //initialization
    println!("b) ptr={} @ {}", *ptr, ptr as usize);

    *ptr *= 10; //modification
    println!("c) ptr={} @ {}", *ptr, ptr as usize);

    dealloc(ptr as *mut u8, layout); //destruction
    println!("d) ptr={} @ {}", *ptr, ptr as usize); //oops, UB here as well

    ptr = null_mut(); //corresponds to nullptr in modern C++
    println!("e) ptr @ {}, null={}", ptr as usize, ptr.is_null());
}
```

```shell
a) ptr=0 @ 100359357848912
b) ptr=42 @ 100359357848912
c) ptr=420 @ 100359357848912
d) ptr=-1268007427 @ 100359357848912
e) ptr @ 0, null=true
```

> **Varning:** Redan steg `a)` läser `*ptr` innan minnet skrivits till - `alloc` ger ett oinitierat minnesblock, så det är också UB, även om det för en enkel `i32` sällan syns i praktiken. Värre är steg `d)`, som läser `*ptr` **efter** att `dealloc` har frigjort minnet. Det är _undefined behavior_ (UB) - precis lika illa som motsvarande use-after-free hade varit i C/C++. Att outputen ser ut som vanliga (om än skräpiga) heltal är ren tur; kompilatorn har i praktiken fria händer vid en UB-punkt, inklusive att optimera bort kod på sätt som gör resultatet oförutsägbart. Jag visar det ändå, för att det är exakt den typen av fotbojor `unsafe` tar bort - och som varken referenser eller säker Rust-kod någonsin hade tillåtit.

> **Notera:** Raden `*ptr = 42;` fungerar här eftersom `i32` saknar `Drop`\-implementation och alla bitmönster är giltiga `i32`\-värden. Det idiomatiska sättet att skriva till nyallokerat, oinitierat minne är annars `std::ptr::write(ptr, 42)` — en vanlig dereferens-tilldelning (`*ptr = value`) tolkas som "droppa det som redan finns på `*ptr`, skriv sedan `value`", och där finns inget giltigt att droppa än. För en typ med en riktig `Drop`\-implementation hade `*ptr = value` alltså varit UB, av samma skäl som ovan.

Variabeln `layout` kommer lite otippat, men innehåller metadata om minnesblockets storlek i antalet bytes (_size_), plus information om blockets adressjustering (_alignment_).

```rust
pub struct Layout {
    size_: usize,
    align_: NonZeroUsize,
}
```

I C++17 kom nya varianter av operatorerna `new` och `delete`, vilka medgav att man kan skicka med förutom antalet bytes, också vilken adressjustering som ska användas. Så här kan det se ut:

```cpp
#include <new>

//operator declaration:
operator new(size_t, std::align_val_t)
operator delete(void*, std::align_val_t) noexcept

//operator usage:
auto ptr = new (std::align_val_t{64}) int{12345};
//...usage of the memory block...
::operator delete(ptr, std::align_val_t{64});
```

(_Anropet av `delete` görs som ett vanligt funktionsanrop eftersom det inte finns någon "placement"-variant av `delete`\-uttrycket i C++ — grammatiken tillåter bara `delete expr`, inga extra argument. Denna `operator delete`\-overload, en s.k. _placement deallocation function_, anropas annars enbart automatiskt av kompilatorn om konstruktorn i ett `placement new`\-uttryck kastar ett undantag._)

Allokerar man ett minnesblock för en sammansatt datatyp, kan det se ut så här.

```rust
let layout = Layout::new::<Point>();
unsafe {
    let ptr: *mut Point = alloc(layout) as *mut Point;
    println!("a) ptr={:?} @ {}", *ptr, ptr as usize);

    (*ptr).x = 10.0;
    (*ptr).y = 5.0;
    println!("b) ptr={:?} @ {}", *ptr, ptr as usize);

    dealloc(ptr as *mut u8, layout);
    println!("c) ptr={:?} @ {}", *ptr, ptr as usize); //oops, UB
}
```

```shell
a) ptr=Point { x: -2.1955198e-7, y: 7e-45 } @ 100359357848912
b) ptr=Point { x: 10.0, y: 5.0 } @ 100359357848912
c) ptr=Point { x: -2.1955198e-7, y: 7e-45 } @ 100359357848912
```

> **Varning:** Precis som i exemplet ovan är steg `c)` UB - `dealloc` har redan frigjort minnet när vi läser `*ptr` igen.

> **Notera:** Samma sak här som i skalar-exemplet ovan - `(*ptr).x = 10.0;` och `(*ptr).y = 5.0;` skriver fält för fält till oinitierat minne. Fungerar eftersom `f32` saknar `Drop`\-implementation, men det idiomatiska sättet är `std::ptr::write(ptr, Point { x: 10.0, y: 5.0 })` - skriv hela värdet på en gång istället för att gå via fältprojektion på oinitierat minne.

> **Notera:** `(*ptr).x` ser ut som klassisk C, men det är faktiskt den enda syntaxen - Rust har ingen arrow-operator (`->`), och till skillnad från referenser gör punktoperatorn ingen auto-deref genom råa pekare. `ptr.x` (utan den explicita `*`) ger `error[E0609]: no field x on type *mut Point`, med kompilatorförslaget "try dereferencing it: (\*ptr).x". Samma sak gäller metodanrop på råa pekare. Auto-deref via `.` är reserverat för referenser och smarta pekare (via `Deref`\-traiten), inte för `*const`/`*mut`.

### `libc::malloc/free`

Rust på Linux länkar dynamiskt libc (standardbiblioteket för C), vilket innebär att man har tillgång till _"good old malloc/free"_. I gengäld måste lejonparten av koden avseende allokering och access ligga inom ett `unsafe`\-block.

```rust
#[derive(Debug)]
struct Point { x: f32, y: f32 }

pub fn main() {
    unsafe {
        let mut ptr: *mut Point = libc::malloc( size_of::<Point>() ) as *mut Point;
        if ptr.is_null() {
            panic!("malloc failed");
        }
        (*ptr).x = 10.0;
        (*ptr).y = 5.0;
        println!("a) ptr={:?} @ {}", *ptr, ptr as usize);

        libc::free(ptr as *mut libc::c_void);
        println!("b) ptr={:?} @ {}", *ptr, ptr as usize); //oops, UB

        ptr = 0 as *mut Point;
        println!("c) ptr: null={} @ {}", ptr.is_null(), ptr as usize);
    }
}
```

```shell
a) ptr=Point { x: 10.0, y: 5.0 } @ 94361111567696
b) ptr=Point { x: 7.3198564e17, y: 7e-45 } @ 94361111567696
c) ptr: null=true @ 0
```

> **Varning:** Samma sak här - steg `b)` läser `*ptr` efter `libc::free`, alltså UB av samma skäl som i `std::alloc`\-exemplen ovan.

* * *

Sammanfattning
--------------

Rust och C++ delar vokabulär - referenser, pekare, mutability - men semantiken skiljer sig rejält under ytan. I Rust är immutable default, och en referens (`&`) är en fullvärdig variabel med egen plats i minnet, inte bara ett kompilatoralias som i C++. Det är också därför en Rust-referens går att peka om (_re-assignment_), medan en C++-referens är låst till sitt mål för alltid. Ownership- och borrow-reglerna - ett mutable-lån åt gången, aldrig blandat med immutable-lån, och lånets livslängd får aldrig överstiga värdets - är kompilatorkontrollerade och stoppar hela kategorier av buggar redan vid `cargo build`, något C++ helt enkelt inte gör åt dig.

Pekare i klassisk mening (`*const`/`*mut`) finns kvar i Rust, men är instängda bakom `unsafe`\-block så fort man vill dereferencera dem. Samma story gäller heap-allokering: `Box` är den rekommenderade vägen och sköter frigörande automatiskt via `drop`, medan `std::alloc` och `libc::malloc/free` ger samma råa kontroll - och samma fallgropar - som i C. Use-after-free och läsning av oinitierat minne är fullt möjligt även i Rust; skillnaden är att språket tvingar en att skriva `unsafe` för att öppna dörren till dem.

* * *

Källkoden
---------

All kod i artikeln finns körklar i ett eget GitHub-repo, länkat sist i länksamlingen nedan.

Rust-sidan byggs med Cargo (varje exempel är ett eget `[[bin]]`):

```bash
cargo build
cargo run --bin reference_demo
```

C++-sidan byggs med CMake:

```bash
cmake -B build-cxx -G Ninja
cmake --build build-cxx
./build-cxx/reference_demo
```
