Structs and classes: C++ and Rust

A C++ class puts data and behaviour in one block and controls access per section. A Rust struct holds only the data, the behaviour lives in an impl block, and access is controlled by modules rather than by a keyword on each member. The two designs produce the same kind of value, and they differ in what a missing field means.

The task in C++

The class has a constructor, a private width and height, and a const method that computes the area. The constructor sets both fields, and the method cannot change them because it is const. This is the smallest complete class, and it is what the Rust struct and impl block will be compared with.

The program also builds a plain aggregate with one of its two fields. C++ fills the rest with zero and warns with -Wextra, and the area of the partial point prints as zero for the missing coordinate.

#include <iostream> class Rectangle { public: Rectangle(int w, int h) : width_(w), height_(h) {} int area() const { return width_ * height_; } private: int width_; int height_; }; struct Point { int x; int y; }; int main() { Rectangle r(3, 4); std::cout << "area: " << r.area() << '\n'; Point p{3}; std::cout << "partial: " << p.x << " " << p.y << '\n'; }

g++ -std=c++23 -Wall -Wextra structs.cpp -o structs
./structs

structs.cpp: In function 'int main()':
structs.cpp:25:14: warning: missing initializer for member 'Point::y' [-Wmissing-field-initializers]
25 | Point p{3};
| ^
area: 12
partial: 3 0

The class part prints twelve. The aggregate part prints three and zero, and the warning is the only sign that a field was left out.

The same task in Rust

The struct declares the two fields, and the impl block holds the constructor as an associated function and the area as a method on &self. There is no private keyword: the fields are visible inside the module that defines them, and the lesson on modules shows how to hide them from other modules.

The program builds the rectangle through the associated function and prints the area. The output matches the C++ class part.

struct Rectangle { width: u32, height: u32, } impl Rectangle { fn new(width: u32, height: u32) -> Self { Self { width, height } } fn area(&self) -> u32 { self.width * self.height } } fn main() { let r = Rectangle::new(3, 4); println!("area: {}", r.area()); }

rustc --edition 2024 structs.rs
./structs

area: 12

The constructor is a convention, not a language feature: any associated function that returns Self does the job, and the lesson on methods builds several.

What differs and why

Who owns the value: the struct owns its fields in both languages, and a struct of integers is a value type that can be copied. In Rust the copy is explicit through the Copy trait, so a struct with a String is moved rather than copied unless the programmer derives Clone.

What the compiler rejects: C++ accepts a partial aggregate initialisation and fills the missing fields with zero. Rust requires every field, and a missing one is E0063 with the field named in the message.

What happens at run time: nothing surprising in either language when every field is set. The C++ partial struct silently has a zero coordinate, which is a value the program may not have intended.

The habit that breaks

The habit is to build a struct with the fields you remember and let the rest default. In Rust that program does not compile, which is the good case: the compiler asks for the value instead of inventing one. The fix is to name the field, take it from another instance with ..base, or implement Default and ask for it.

struct Point { x: i32, y: i32, } fn main() { let p = Point { x: 3 }; println!("partial: {} {}", p.x, p.y); }

rustc --edition 2024 partial.rs

error[E0063]: missing field `y` in initializer of `Point`
--> partial.rs:7:13
|
7 | let p = Point { x: 3 };
| ^^^^^ missing `y`

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0063`.

The C++ lesson for this ground is Classes and objects, and the Rust lesson is Structs and field initialisation. Methods are the next Rust lesson, impl, self and associated functions.

The C++ program was built with g++ 15.2.0 and the Rust program with rustc 1.96.1, and both were run on Alpine Linux 3.24.3 (x86_64) in a Docker container.

Article author: Arthur Isaev