new_blog/posts/rust-cpp-comparison.md
2025-05-21 22:49:12 -04:00

6.1 KiB

title description date draft tags
Equivalencies between Rust and C++ Ever get annoyed at someone being wrong on the internet and concluded the best way to deal with it is to do what they're doing but correctly? 2025-05-21 true

Saw a Rust vs C++ cheatsheet which was wrong. In response to this I'm just going to write something that isn't a cheatsheet but still compares the 2 languages.

Unless stated otherwise code snippets are at the top level of the file, e.g. not inside a function body. Additionally all needed C++ #include or import std statements exist above the code block.

Table of Contents

TODO: make these links

  • Hello World
  • Variables
  • Copying and Moving
  • Basic Types
  • Loops
  • Functions
  • Making Your Own Types
  • Type Aliasing
  • Compile time computation
  • Generics
  • Modularity
  • Dependency Management and Build Systems
  • Non-linear control flow
  • Meta Programming
  • Pointers, References and Lifetimes

Hello World

Commentary

Nothing to interesting just a nice way to dip our toes in the water.

C++

int main(){
    std::cout << "Hello World!" << std::endl;
    // Alternatively for C++23 and onwards
    std::println("Hello World!");
}

Rust

fn main(){
    println!("Hello World!");
}

Variables

Commentary

Here is where things start to get interesting, in C++ variables are mutable by default, in Rust they are immutable by default. Both snippets below are both inside a function body not shown.

C++

// immutable variable via const keyword
const int x = 0;

// mutable variable implicitly
int y = 0;

// compiler error due to x being immutable
x = 5;

// allowed due to y being mutable
y = 5;

// we can also infer the type of a variable via auto
auto z = 3;

Rust

// immutable implicitly
let x: i32 = 0;

// mutable variable via mut specifier
let mut y:i32 = 0;

// compiler error due to x being immutable
x = 5;

// allowed due to y being mutable
y = 5;

// type inferred
let z = 3;

// There isn't an obvious C++ equivalent but this is
// useful and common enough to make an example of
//
// we aren't changing x here we're making a new variable
// which is named x, past this point any code referring
// to x refers to this new x
//
let x = "variable shadowing";

Copying and Moving

Commentary

C++ has copying values as the default, Rust has moving values as the default. These defaults are reflected in how copying and moving work in the respective languages.

All of this code is within a function body

C++

Copying in C++ will happen when you assign a variable if you don't take action to prevent it. The following will result in a copy.

std::vector<int> first = {1,2,3};

// second is copy of first which will store copies of 1, 2 and 3
// leaving first untouched
std::vector<int> second = first;

In order to move a value however particular action needs to be taken.

// unique_ptr used to avoid needing to make my own type
std::vector<std::unique_ptr<int>> first = {std::make_unique(1), std::make_unique(2), std::make_unique(3)};

// second takes the values in first for it's own and first is left
// a husk of it's former self
std::vector<std::unique_ptr<int>> second = std::move(first);

// we can still use first though even though we're just dealing
// with a husk of a value
second = std::move(first)

Rust

In Rust we have the opposite phenomenon, a value will be moved if you don't take action to prevent it.

// we can't do type inference due to not having enough information
let first:Vec<i32> = vec![1,2,3];

// first is moved into second
let second = first;

// unlike C++ Rust will not allow usage of a value which has been
// moved, the line below will be a compiler error
let third = first;

There are exceptions to this, some types are annotated to indicate that copying them bit for bit is valid, for values of those types instead of a move occuring when they are assigned they will simply have a copy made.

Most primitive types have this annotation. Types which don't have the annotation can still be copied however. The copying is just explicit most of the time this is done via a call to clone. See #Generics(TODO: hyperlink) for details on what the bit copy annotation is and the way you're supposed to add a clone method to your own types.

let first: Vec<i32> = vec![1,2,3];

let second = first.clone();

// won't be a compiler error due to first not having moved when
// we assigned second
let third  = first;

Basic Types

Nothing interesting, here's a table providing info on equivalent types.

If a C++ type has std:: at the front it isn't a primitive.

C++ Rust
bool bool
char no equivalent
short no equivalent i16 is the closest
int no equivalent i32 is the closest
long no equivalent i32 is the closest
long long no equivalent i64 is the closest
unsigned ... u...
std::int(8,16,32,64)_t i(8,16,32,64)
std::uint(8,16,32,64)_t u(8,16,32,64)
float f32
double f64
std::size_t usize
std::ptrdiff_t (not touching ssize_t thanks) isize
no equivalent std::uint32_t is the closest char
no equivalent std::string_view is the closest str
void no equivalent () is the closest
no equivalent void, std::monostate and std::tuple<> are the closest ()
no equivalent [[noreturn]] void is the closest ! (nightly at time of writing)

Loops

TODO

Commentary

C++

Rust

Functions

TODO

Commentary

C++

Rust

Making Your Own Types

TODO

Commentary

C++

Rust

Type Aliasing

TODO

Commentary

C++

Rust

Compile time computation

TODO

Commentary

C++

Rust

Pattern Matching

TODO

Commentary

C++

Rust

Generics

TODO

Commentary

C++

Rust

Modularity

TODO

Commentary

C++

Rust

Dependency Management and Build Systems

TODO

Commentary

C++

Rust

Fancy Functions

TODO

Commentary

C++

Rust

Meta Programming

TODO

Commentary

C++

Rust

Pointers, References and Lifetimes

TODO

Commentary

C++

Rust