C++ vs Rust

A random collection of differences

Alex Badics (admin@stickman.hu)

About me

  • Alex Badics
  • Developer and PM
  • Opinions are my employer's

admin@stickman.hu
alex@voidcomputing.hu

About this presentation

  • Many high level comparisons out there
  • I won't talk (much) about
    • Ownership semantics
    • Borrow checker
    • HM typesystem
    • Error handling
    • Macros

Const, mut, copy, move

C++


                            void f1(T &a);
                            void f2(const T &a);
                            void f3(T a);
                            void f3r(T &&a);

                            /*...*/
                            T val;

                            f1(val);
                            f2(val);
                            f3(val);
                            f3r(std::move(val));
                        

Rust


                            fn f1(a: &mut T);
                            fn f2(a: &T);
                            fn f3(a: T);


                            /*...*/
                            let val = T::new();

                            f1(&mut val);
                            f2(&val);
                            f3(val.clone());
                            f3(val);
                        

Type inference

C++


                            auto vec = std::vector<int>();
                            vec.push_back(5);
                            auto val = vec[0];
                        

Rust


                            let vec = Vec::<i32>::new();
                            vec.push(5);
                            let val = vec[0];
                        

Still Rust


                            fn int_returner() -> i32 {
                                let vec = Vec::new();
                                vec.push(5);
                                return vec[0];
                            }
                        

Iterators

The problem


                        auto even = std::find_if(
                            vec1.begin(),
                            vec2.end(),
                            is_even
                        );
                    

Iterators

C++


                            template<typename It>
                            std::vector<int>
                            squares(It first, It last) {
                                std::vector<int> result;
                                result.reserve(
                                    std::distance(first, last)
                                );
                                std::transform(
                                    first,
                                    last,
                                    std::back_inserter(result),
                                    [](int i){return i * i;}
                                );
                                return result;
                            }
                        

Rust


                            pub fn squares<It>(it: It) -> Vec<i32>
                                where It: Iterator<Item=i32>
                            {
                                it.map(|i| i * i).collect()
                            }
                        

Iterators

C++23


                            std::vector<int>
                            squares(std::ranges::input_range auto&& range) {
                                return range
                                    | std::views::filter([](int i){ return i % 2 == 0; })
                                    | std::views::transform([](int i){ return i * i; })
                                    | std::ranges::to<std::vector>();
                            }
                        

Mutexes

C++11


                            void stuff_sync(std::mutex mtx, Data &data) {
                                auto prepared = prepare_things();
                                {
                                    std::unique_lock<std::mutex>(mtx);

                                    stuff(prepared, data);
                                }
                                unprepare(prepared);
                            }
                        

Rust



                            fn stuff_sync(data: &mut Mutex<Data>) {
                                let prepared = prepare_things();
                                {
                                    let locked_data =
                                        data.lock().unwrap();
                                    stuff(&prepared, &*locked_data);
                                }
                                unprepare(prepared);
                            }
                        

Memory aliasing

C


                            void add16x(int *to, const int *from) {
                                *to = *from;
                                for(int i=0; i<15; ++i) {
                                    *to += *from;
                                }
                            }
                        

Rust


                            fn add16x(to: &mut isize, from: &isize) {
                                *to = *from;
                                for i in 0..15 {
                                    *to += *from;
                                }
                            }
                        

Memory aliasing

C


                            void add16x(int *to, const int *from) {
                                *to = *from;
                                for(int i=0; i<15; ++i) {
                                    *to += *from;
                                }
                            }

                            int add_test() {
                                int a = 1;
                                add16x(&a, &a);
                                return a;
                            }
                        

Rust


                            fn add16x(to: &mut isize, from: &isize) {
                                *to = *from;
                                for i in 0..15 {
                                    *to += *from;
                                }
                            }

                            fn add_test() -> isize{
                                let a = 1;
                                add16x(&mut a, &a);
                                return a;
                            }
                        

Memory aliasing

C


                            void add16x(int *to, const int *from) {
                                *to = *from;
                                for(int i=0; i<15; ++i) {
                                    *to += *from;
                                }
                            }
                        

                        add16x:
                            mov     eax, dword ptr [rsi]
                            add     eax, eax
                            mov     dword ptr [rdi], eax
                            add     eax, dword ptr [rsi]
                            # ....
                            mov     dword ptr [rdi], eax
                            add     eax, dword ptr [rsi]
                            mov     dword ptr [rdi], eax
                            ret
                        

Rust


                            fn add16x(to: &mut isize, from: &isize) {
                                *to = *from;
                                for i in 0..15 {
                                    *to += *from;
                                }
                            }
                        

                            mov     eax, dword ptr [rsi]
                            shl     eax, 4
                            mov     dword ptr [rdi], eax
                            ret
                        

Memory aliasing

restrict


                            void add16x(int *restrict to, const int *from) {
                                *to = *from;
                                for(int i=0; i<15; ++i) {
                                    *to += *from;
                                }
                            }
                        

                            mov     eax, dword ptr [rsi]
                            shl     eax, 4
                            mov     dword ptr [rdi], eax
                            ret
                        

VTables

C++


                            struct BaseA {
                                virtual ~BaseA(){};
                                virtual void do_stuff() = 0;
                                virtual void undo_stuff() = 0;
                            };

                            struct BaseB {
                                virtual ~BaseB(){};
                                virtual void thing() = 0;
                            };


                            struct Derived: BaseA, BaseB {
                                void do_stuff() override{/* */}
                                void undo_stuff() override {/* */}
                                void thing() override {/* */}
                            }
                        

Rust


                            pub trait BaseA{
                                fn do_stuff(&mut self);
                                fn undo_stuff(&mut self);
                            }

                            pub trait BaseB{
                                fn thing(&mut self);
                            }

                            pub struct Derived {
                                /* ... */
                            }

                            impl BaseA for Derived {
                                fn do_stuff(&mut self) {/**/}
                                fn undo_stuff(&mut self) {/**/}
                            }

                            impl BaseB for Derived {
                                fn thing(&mut self) {/**/}
                            }
                        

VTables

C++


                            void dyn_param(BaseB& o) {
                                o.thing();
                            }

                            void tpl_param(std::derived_from<BaseB> auto &o) {
                                o.thing();
                            }

                            void caller() {
                                Derived d;
                                dyn_param(d);
                                tpl_param(d);
                            }
                        

Rust


                            pub fn dyn_param(o: &mut dyn BaseB) {
                                o.thing();
                            }

                            pub fn tpl_param(o: &mut impl BaseB) {
                                o.thing();
                            }

                            pub fn caller() {
                                let mut d = Derived{/**/};
                                dyn_param(&mut d);
                                tpl_param(&mut d);
                            }
                        

VTables

C++


                            Derived derived;
                            BaseA &base_a=derived;
                            BaseB &base_b=derived;
                        

VTables

Rust


                            let derived = Derived{/**/};
                            let base_a: &BaseA = &derived;
                            let base_b: &BaseB = &derived;
                        

Type of functions

C++


                            void fn1(unsigned a);
                            void fn2(unsigned a);

                            void fn_ptrs() {
                                auto fp = fn1;
                                fp = fn2;
                                fp(5);
                            }
                        

Rust


                            fn fn1(a: usize){}
                            fn fn2(a: usize){}

                            fn fn_ptrs(){
                                let mut fp = fn1;
                                fp = fn2;
                                fp(5);
                            }
                        

Type of functions


                            error[E0308]: mismatched types
                             --< >source<:6:10
                              |
                            5 |     let mut fp = fn1;
                              |                  --- expected due to this value
                            6 |     fp = fn2;
                              |          ^^^ expected fn item, found a different fn item
                              |
                              = note: expected fn item `fn(_) {fn1}`
                                         found fn item `fn(_) {fn2}`
                              = note: different fn items have unique types, even if their signatures are the same
                              = help: consider casting both fn items to fn pointers using `as fn(usize)`
                        

Type of functions

C++


                            void ptr_arg(void (*f)(unsigned)) {
                                f(5);
                            }

                            template<typename T>
                            void dyn_arg(T f) {
                                f(5);
                            }

                            template<void (*f)(unsigned)>
                            void tpl_arg() {
                                f(5);
                            }

                            void caller() {
                                ptr_arg(fn1);
                                dyn_arg(fn1);
                                tpl_arg<fn1>();
                            }
                        

Rust


                            fn ptr_arg(f: fn(usize)){
                                f(5);
                            }


                            fn dyn_arg(f: &dyn Fn(usize)){
                                f(5);
                            }


                            fn tpl_arg(f: impl Fn(usize)){
                                f(5);
                            }

                            pub fn caller(){
                                ptr_arg(fn1);
                                dyn_arg(&fn1);
                                tpl_arg(fn1);
                            }
                        

Thanks!

We talked about:

  • Type inference
  • Iterators
  • Mutexes
  • Memory aliasing
  • Vtables
  • Function types

Send complaints to
admin@stickman.hu
alex@voidcomputing.hu