C++ to Rust: Key Differences in Move Semantics, Option Type, and Iterator
The article compares C++ and Rust for developers switching languages, explaining how Rust enforces move semantics instead of copy defaults, uses the safe Option enum with exhaustive pattern matching instead of nullable pointers, and favors lazy, chainable iterators over index‑based loops for performance and safety.
This article explores the key differences between C++ and Rust from the perspective of a C++ developer transitioning to Rust, focusing on three major areas: move semantics, Option type handling, and Iterator patterns.
1. Move Semantics
In C++, assignment operations use copy semantics by default - after assignment, both the original and new objects exist independently. Modifying one does not affect the other. Rust provides more granular control: types implementing the Copy trait use copy semantics, while others use move semantics.
In Rust, the compiler enforces move semantics at compile time. When a variable's value is moved, it cannot be used again:
fn main() { let mut x = 5; let rx0 = &mut x; let rx1 = rx0; println!("test {}", rx0); }
This produces a compile error: "borrow of moved value: `rx0`". In contrast, C++ requires explicitly disabling copy constructors to achieve similar behavior, and using std::move can lead to runtime crashes if not handled carefully.
For references, immutable references (&T) implement Copy, while mutable references (&mut T) do not. This makes mutable references "exclusive" - each assignment invalidates the old variable, ensuring only one mutable reference exists at a time.
2. Option and Pattern Matching
In C++, optional values are typically handled using pointers (including smart pointers like shared_ptr and unique_ptr), with null checks at runtime. Rust uses enum and pattern matching for compile-time safety.
Rust's enum is similar to C++'s union but with tagged variants:
enum Message { Quit, Move {x: i32, y: i32}, Write (String), }
The match expression requires exhaustive pattern matching - the compiler ensures all enum variants are handled:
match message { Message::Quit => todo!(), Message::Move { x, y } => todo!(), Message::Write(info) => todo!(), }
Option<T> is Rust's solution for nullable values:
pub enum Option<T> { /// No value None, /// Some value `T` Some(T), }
Using Option forces developers to handle both Some and None cases through pattern matching, preventing null pointer dereferences at compile time.
3. Iterator
Rust emphasizes iterators over index-based loops for efficiency and safety. Using iterators avoids redundant bounds checking:
for v in data { println!("{} ", v); }
Vec provides three iterator methods: iter() returns &T (non-consuming), iter_mut() returns &mut T (non-consuming), and into_iter() returns T (consuming).
Iterators support adapter patterns for chain operations: map, filter, filter_map, and more. These adapters are lazy - they don't execute until next() is called:
v.into_iter().filter(even).map(square).take(2);
Lazy evaluation enables infinite iterators and optimizes performance by only processing elements as needed.
Tencent Cloud Developer
Official Tencent Cloud community account that brings together developers, shares practical tech insights, and fosters an influential tech exchange community.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.