Type equivalents

The type equivalents listed in this document are equivalent for the purposes of programming in Rust as one would program in C++. They are not necessarily equivalent in terms of being useful for interacting with C or C++ programs via an FFI. For types that are useful for interoperability with C or C++, see the Rust std::ffi module documentation and the FFI documentation in the Rustonomicon.

Primitive types

Integer types

In C++, many of the integer types (like int and long) have implementation defined widths. In Rust, integer types are always specified with their widths, much like the types in <cstdint> in C++. When it isn't clear what integer type to use, it is common to default to i32, which is the type that Rust defaults to for integer literals.

C++ typeRust type
uint8_tu8
uint16_tu16
uint32_tu32
uint64_tu64
int8_ti8
int16_ti16
int32_ti32
int64_ti64
size_tusize
isize

In C++ size_t is conventionally used only for sizes and offsets. The same is true in Rust for usize, which is the pointer-sized integer type. The isize type is the signed equivalent of usize and has no direct equivalent in C++. The isize type is typically only used to represent pointer offsets.

Floating point types

As with integer types in C++, the floating point types float, double, and long double have implementation defined widths. C++23 introduced types guaranteed to be IEEE 754 floats of specific widths. Of those, float32_t and float64_t correspond to what is usually expected from float and double. Rust's floating point types are analogous to these.

C++ typeRust type
float16_t
float32_tf32
float64_tf64
float128_t

The Rust types analogous to float16_t and float128_t (f16 and f128) are not yet available in stable Rust.

Raw memory types

In C++ pointers to or arrays of char, unsigned char, or byte are used to represent raw memory. In Rust, arrays ([u8; N]), vectors (Vec<u8>), or slices (&[u8]) of u8 are used to accomplish the same goal. However, accessing the underlying memory of another Rust value in that way requires unsafe Rust. There are libraries for creating safe wrappers around that kind of access for purposes such as serialization or interacting with hardware.

Character and string types

The C++ char or wchar_t types have implementation defined widths. Rust does not have an equivalent to these types. When working with string encodings in Rust one would use unsigned integer types where one would use the fixed width character types in C++.

C++ typeRust type
char8_tu8
char16_tu16

The Rust char type represents a Unicode scalar value. Thus, a Rust char is the same size as a u32. For working with characters in Rust strings (which are guaranteed to be valid UTF-8), the char type is appropriate. For representing a byte, one should instead use u8.

The Rust standard library includes a type for UTF-8 strings and string slices: String and &str, respectively. Both types guarantee that represented strings are valid UTF-8. The Rust char type is appropriate for representing elements of a String.

Because str (without the reference) is a slice, it is unsized and therefore must be used behind a pointer-like construct, such as a reference or box. For this reason, string slices are often described as &str instead of str in documentation, even though they can also be used as Box<str>, Rc<str>, etc.

Rust also includes types for platform-specific string representations and slices of those strings: std::ffi::OsString and &std::ffi::OsStr. While these strings use the OS-specific representation, to use one with the Rust FFI, it must still be converted to a CString.

Unlike C++ which has std::u16string, Rust has no specific representation for UTF-16 strings. Something like Vec<u16> can be used, but the type will not guarantee that its contents are a valid UTF-16 string. Rust does provide a mechanisms for converting String to and from a UTF-16 encoding (String::encode_utf16 and String::from_utf16, among others) as well as similar mechanisms for accessing the underlying UTF-8 encoding (https://doc.rust-lang.org/std/string/struct.String.html#method.from_utf8).

PurposeRust type
representing textString and &str
representing bytesvectors, arrays, or slices of u8
interacting with OSOsString and &OsStr
representing UTF-8String
representing UTF-16use a library

Boolean types

The bool type in Rust is analogous to the bool type in C++. Unlike C++, Rust makes guarantees about the size, alignment, and bit pattern used to represent values of the bool type.

void

In C++ void indicates that a function does not return a value. Because Rust is expression-oriented, all functions return values. In the place of void, Rust uses the unit type (). When a function does not have a return type declared, () is the return type.

#include <iostream>

void process() {
    std::cout
        << "Does something, but returns nothing."
        << std::endl;
}
#![allow(unused)]
fn main() {
fn process() {
    println!("Does something but returns nothing.");
}
}

Since the unit type has only one value (also written ()), values of the type provide no information. This also means that the return value can be left implicit, as in the above example. The following example makes the unit type usage explicit.

#![allow(unused)]
fn main() {
fn process() -> () {
    let () = println!("Does something but returns nothing.");
    ()
}
}

The syntax of the unit type and syntax of the unit value resemble that of an empty tuple. Essentially, that is what the type is. The following example shows some equivalent types, though without the special syntax or language integration.

struct Pair<T1, T2>(T1, T2); // the same as (T1, T2)
struct Single<T>(T); // a tuple with just one value (T1)
struct Unit; // the same as ()
// can also be written as
// struct Unit();

fn main() {
    let pair = Pair(1,2.0);
    let single = Single(1);
    let unit = Unit;
    // can also be written as
    // let unit = Unit();
}

Using a unit type instead of void enables expressions with unit type (such as function calls that would return void in C++) to be used in contexts that expect a value. This is especially helpful with defining and using generic functions, instead of needing something like std::is_void to special-case the handling when a type is void.

Pointers

The following table maps the ownership-managing classes from C++ to equivalents types in Rust.

UseC++ typeRust type
OwnedTT
Single owner, dynamic storagestd::unique_ptr<T>Box<T>
Shared owner, dynamic storage, immutable, not thread-safestd::shared_ptr<T>std::rc::Rc<T>
Shared owner, dynamic storage, immutable, thread-safestd::shared_ptr<T>std::sync::Arc<T>
Shared owner, dynamic storage, mutable, not thread-safestd::shared_ptr<T>std::rc::Rc<std::cell::RefCell<T>>
Shared owner, dynamic storage, mutable, thread-safestd::shared_ptr<std::mutex<T>>std::sync::Arc<std::mutex::Mutex<T>>
Const referenceconst &T&T
Mutable reference&T&mut T
Const observer pointerconst *T&T
Mutable observer pointer*T&mut T

In C++, the thread safety of std::shared_ptr is more nuanced than it appears in this table (e.g., some uses may require std::atomic). However, in safe Rust the compiler will prevent the incorrect use of the shared owner types.

Unlike with C++ references, Rust can have references-to-references. Rust references are more like observer pointers than they are like C++ references.

void*

Rust does not have anything directly analogous to void* in C++. The upcoming chapter on RTTI will cover some use cases where the goal is dynamic typing. The FFI chapter of the Rustonomicon covers some use cases where the goal is interoperability with C programs that use void*.

Containers

Both C++ and Rust containers own their elements. However, in both the element type may be a non-owning type, such as a pointer in C++ or a reference in Rust.

C++ typeRust type
std::vector<T>Vec<T>
std::array<T, N>[T; N]
std::list<T>std::collections::LinkedList<T>
std::queue<T>std::collections::VecDeque<T>
std::deque<T>std::collections::VecDeque<T>
std::stack<T>Vec<T>
std::map<K,V>std::collections::BTreeMap<K,V>
std::unordered_map<K,V>std::collections::HashMap<K,V>
std::set<K>std::collections::BTreeSet<K>
std::unordered_set<K>std::collections::HashSet<K>
std::priority_queue<T>std::collections::BinaryHeap<T>
std::span<T>&[T]

For maps and sets instead of the container being parameterized over the hash or comparison function used, the types require that the key types implement the std::hash::Hash (unordered) or std::cmp::Ord (ordered) traits. To use the containers with different hash or comparison functions, one must use a wrapper type with a different implementation of the required trait.

Some C++ container types provided by the STL have no equivalent in Rust. Many of those have equivalents available in third-party libraries.

One significant different in the use of these types between C++ in Rust is with the Vec<T> and array [T; N] types, from which slice references &[T] or &mut [T] to part or all of the data can be cheaply created. For this reason, when defining a function that does not modify the length of a vector and does not need to statically know the number of elements in an array, it is more idiomatic to take a parameter as &[T] or &mut [T] than as a reference to the owned type.

In C++ it is better to take begin and end iterators than a span when possible, since iterators are more general. The same is true with Rust and taking a generic type that implements IntoIter<&T> or IntoIter<&mut T> instead of &[T].

#include <iterator>
#include <vector>

template <typename InputIter>
void go(InputIter first, InputIter last) {
  for (auto it = first; it != last; ++it) {
    // ...
  }
}

int main() {
  std::vector<int> v = {1, 2, 3};
  go(v.begin(), v.end());
}
use std::iter::IntoIterator;

fn go<'a>(iter: impl IntoIterator<Item = &'a mut i32>) {
    for x in iter {
        // ...
    }
}

fn main() {
    let mut v = vec![1, 2, 3];
    go(&mut v);
}