Rust, Swift and C++

Meqt

A simple Comparsion on Swift, Rust, C++’s syntax as a beginner on Rust after learning some swift and C/C++. Three static-typed, compiled programming language .

Besides, as swift 5.9 comes with C++ interoperability support I think it would be a good source to learn some C++ and Swift relationship and comparision.

Value and Types

It’s infact impossible to talk about value, without talking about how do functions, closure, structure, class, and ARC d

Swift value basics

1
2
3
let x = 1
var y = x
print("\(x), \(y)")

Rust with owership

After I write this code, I believe rust should be safer and faster with it’s ownership, reference and borrowing.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fn main() {
let x = 1;
let mut y = x;
let mut z = x;

let rx1 = &x;
let mut rx2 = &x;
/* error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
let rx3 = &mut x;
*/
let ry1 = &y;
let ry2 = &y;
/* error[E0502]: cannot borrow `y` as mutable because it is also borrowed as immutable
let ry_err = &mut y;
println!("{}, {}, {}", ry1, ry2, ry_err);
*/
println!("{}, {}", ry1, ry2);

let rz1 = &mut z;
/* error[E0499]: cannot borrow `z` as mutable more than once at a time
let rz2 = &mut z;
println!("{}, {}", rz1, rz2);
*/
}

Swift Memory Operation

while in swift you don’t expecilitly have a reference for what ever value you want, but class, and ARC is a thing to talk about.

Someone said that swift don’t have directly access to Memory which makes it slower. I don’t know if it’s becaused my misundering, though uncommon, but we have for array:

1
2
3
4
5
6
7
8
9
10
11
var arr = [10, 100, 1000, 10000]
mutating func withUnsafeMutableBufferPointer<R>(_ body: (inout UnsafeMutableBufferPointer<Element>) throws -> R) rethrows -> R
arr.withUnsafeMutableBufferPointer { ptr in
<#code#>
}

let constArr = [10, 100, 1000, 1000]
constArr.withUnsafeBufferPointer { ptr in
<#code#>
}
func withUnsafeBufferPointer<R>(_ body: (UnsafeBufferPointer<Element>) throws -> R) rethrows -> R

and

1
2
3
4
5
6
7
8
9
let arr = [10, 20]
withUnsafePointer(to: arr) { ptr in
<#code#>
}

var mutableArr = [10, 20]
withUnsafeMutablePointer(to: &mutableArr) { ptr in
<#code#>
}

Nothat these ptr are different, one is type method for array, with BufferPointer; and another is Generic function and Pointer.
This is not talked about in Swift Programming Language, but have a detailed explaination in Standard Library Documentation

These function would be helpful when coping with c code. But if not, you may not ever see these code in swift. Anyway, it’s a big topic, but a glimpses of it makes me have a better understanding of swift programming language. And I hope this would be helpful for anyone learn these language.

stored property, calculated property, and property observer.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var stored: Int = 0  {
willSet {
}
didSet {
}
}

var getOnlyComputedValue: Int {
get {
return stored
}
}

// var getOnlyComputedValue: Int {
// stored
// }

var settableComputedValue: Int {
get {
getOnlyComputedValue
}
set {
stored += 1
}
}

I think swift struct and class should also be talked about in this phase, for accordance, and better understanding with let and val, and let, var for compound value and reference.

Type

Both in swift and rust, thougth staticly typed. Many times you don’t expilictly write type, and type Inferenced is the default behavior, unlike c/c++, thought you have auto and decltype, it’s not the default behavior, thus most times you still need to write types.

But still, swift and rust have some difference in type inference:

1
2
3
4
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);

this phase is legal, as type of scores is infered from line below.

How, similar code is impossible in swift, in swift these code are ok, as the type can but infered from literal after the assignment sign.

1
2
3
4
5
6
let arr = [10, 100, 1000] // OK
var dict = [
"Blue": 10,
"Yellow": 50,
"Green": 100
] // OK

but in swift, type can’t be infered from line after

1
2
var dict = Dictionary() //Error: Generic parameter 'Key' could not be inferred
dict["Blue"] = 10

Believe, this won’t cause much trouble in swift, and I believe would your code more clear.

What’s more, I think this limited make’s swift repl possible.
I think in Rust this type inference would cause trouble in implementation of repl, as you can do this when writing in a file, types are infered from line after, but in repl, when you write code line by line, this feature was impossible. But it seems Rust really have a repl thing, maybe they just disabled this feature in REPL, I don’t know.

Struct, Class

Some Language have only struct like C, some only class like Java, maybe python, I don’t rememeber any python code with struct. Up to my knowledge know, Rust has only Struct. C++ have both struct and class, but it’s kind of redundancy. While in swift, struct and class have core difference, struct is value type and class is reference type, this is what I found unique for swift(up to my knowledge now), and thought that it’s for this reason, in swift, c++ interoperatability, c++ struct and class are both mapped to struct in swift, still swift struct and c++ class aren’t equivalent.

And in swift, thus comes ARC for Class.

In rust, struct and impl are seprated, while in swift, you can have methods defined inside struct.

And when it comes to c++, it’s awkwardly complicated, powerful, efficient.

Expressions, Statement, and semicolons

Functions and maybe Closure

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func foo(input val: Int) {
print(val)
}

func foo(_ val: inout Int) -> Int {
let oldValue = val
val += 1
print(val)
return oldValue
}

let x = 1
foo(input: 1)
foo(input: x)
var y = 1
let output = foo(&y)
print(output)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
fn foo(s: String) {
print("{}", s);
}

fn first_word_slice(s: &mut String) -> &str {
s.push_str(" world.");
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}

fn main() {
let mut s1 = String::from("hello");
let word1 = first_word_slice(&mut s1);
println!("the first word is: {}", word1);
}

Enum

Option

1
2
3
let some_number = Some(5);
let some_char = Some('e');
let absent_number: Option<i32> = None;
1
2
var x: Int? = nil
let y = 1

I’m not sure, but much thing beside for Option in Rust and Swift are same.

First when I saw, ?, !, ??, try?, try? if let, guard let, in Swift Programmming Language, it’s makes me feel weird. I’ve now get used to it, and I thought this syntax is helpful and persures clarity.

Pattern Matching

  • Rust is with match synatx
  • Swift use switch/case
    both with assocative value, and execustive match

Packages, Crates and Modules

Swift

In swift, we have swift package manager. We got Packages, targets and module is something your didn’t always talk about but used all the time.

One thing I like about this swift package manager is Package.swift that enable your to write swift directly in manifest.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
 // swift-tools-version: 5.8
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "MyBufferTests",
platforms: [
.macOS(.v13),
],
products: [ ],
dependencies: [ ],
targets: [
.target(
name: "CmmulMEQT",
cSettings: [
.unsafeFlags(["-O3"]),
.define("ACCELERATE_NEW_LAPACK")
],
linkerSettings: [
.linkedFramework("Accelerate")
]
),
.executableTarget(
name: "SwiftBufferTests",
dependencies: [
"CmmulMEQT"
],
swiftSettings: [
.unsafeFlags(["-O"])
]
),
.executableTarget(
name: "CBufferTests",
dependencies: [
"CmmulMEQT"
],
cSettings: [ .unsafeFlags(["-O3"]) ]
),
],
cLanguageStandard: .c17,
cxxLanguageStandard: .cxx20
)

and you have import syntax

1
import Foundation

Rust

In rust, your have crago for Packages, crates and modules. You write cargo.toml for manifest.

and have syntax like

1
use std::collections::HashMpa;

C++

it’s like somewhat in c++

1
using namespace std;

and

1
2
#include <vector>
using std::vector;

in c/c++, get files into multiple files is something I don’t like, your need header and something iterative and replicatant.

Interlude

1
2
3
4
5
let first = &v[0];

//Error: v.push(6);

println!("The first element is : {first}");

I didn’t releaize the problem with this code at first point, I’m far from being rusty. 🤕

Common Collections

Strings

Both Swift and Rust String are complicated because of Unicode and UTF-8/UTF-16 encoding, so unlike when you are coping with ASCII in C/C++, string indexing may not be as simple, but more power and multi-language friendly.

Array

Error Handling

Rust

In rust, you got

1
2
3
4
enum Result<T, E> {
Ok(T),
Err(E),
}

it’s explicate enum, just like Option, with functions like: .unwrap(), .expect(); Besides, you got panic!, and ?` for Error propagation.

Let’s review some words in Rust and Swift

Rust Swift
Cargo Swift Package Manager
crate Target
panic fatalError
trait Protocol
it seems rustaceans really like kind of naming.

Swift

While in swift, you have got more some what jargon, keyword and syntax to know about. Optional is not really a part, but it’s something I found fit here.

First you got

1
2
3
4
5
6
7
func functionThatThrows() -> throws Int {
throw Error("Some Erro")
return 0
}
// and you call it with
let val = try? functionThatThrows()
let nextVale = try! functionThatThrows()

you have try, throws, throw and rethrow, combines with ?, !, ?? or without this postfix, to make a great number of combinations of easy to remember, intuitive and handy things.

1
2
3
4
5
6
7
8
do {
// some operation that throws
let val = try functionThatThrows()
}catch someError {
// some code to save or not
}catch anotherError where ... {
//
}

and some handy ones for optional, optional and error handling are something interconnection in swift.

1
2
3
4
5
6
7
8
9
10
11
12
13
let val: Int? = 1 
if let x = val {
// some code
}else{
// some other code
}
//❌: print(x), x out of scope

guard let y = val else{
fatalError("...")
// or return
}
print(y) // ✅

and another way to do error handling is just the programming crash.
for example some times you can write code let that but not recommanded.

1
2
3
4
let val: Int? = nil
let y = val!
precondition(1 == 0, "🧐")
fatalError("Let's crash")

and code run only for debug

1
assert(1 == 0, "Only prints when debuging")

you can see both Result and Option have mapping one to swift, but instead of explicitly using enum, swift introduces keywords to make it handy.

C++

C++ also have try, catch blocks and assert, static_assert. But not Optional, nullptr instead.

Generic & Trait, Generic & Protocols, Generic & Concept

Trait, Protocol are like interface in other languages, for example Java.

Core Difference of Rust and Swift Generics vs. C++ Generrics
let’s have a look at the code:

1
2
3
4
5
6
7
8
9
10
// ❌
fn largest<T>(list: &[T]) -> &T {
let mut largest = &list[0];
for item in list {
if item > largest {
largest = item;
}
}
largest
}
1
2
3
4
5
6
7
8
9
10
// ❌
func largest<T>(among arr: [T]) -> T {
var maximum = arr[0]
for val in arr {
if val > maximum {
maximum = val
}
}
return maximum
}

If you try to compile these two functions, you would get an error.
but in c++

1
2
3
4
5
6
7
8
9
10
11
12
13
// ✅🧐
template<typename T>
const T& largest(const std::vector<T>& list) {
const T* largest = &list[0];

for (const T& item : list) {
if (item > *largest) {
largest = &item;
}
}

return *largest;
}

but in C++, it works.

The reason it doesn’t work is that in Rust, and Swift, > is not garuranted to work on T, the same is true for c++, but c++’s generic have maybe something like lazy evluation, so it’s not checkout until you write code that fill in some real concreate type in T. It won’t cause error, but make troubles in developing. These we have

Concept in C++23

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
#include <vector>
#include <concepts>

template<typename T>
concept Comparable = requires(T a, T b) {
{ a > b } -> std::convertible_to<bool>;
};

template<Comparable T>
const T& largest(const std::vector<T>& list) {
const T* largest = &list[0];

for (const T& item : list) {
if (item > *largest) {
largest = &item;
}
}

return *largest;
}

int main() {
std::vector<int> nums = { 1, 5, 2, 8, 3 };
int largestNum = largest(nums);
std::cout << "Largest number: " << largestNum << std::endl;

std::vector<double> doubles = { 1.2, 3.5, 2.8, 0.5, 4.7 };
double largestDouble = largest(doubles);
std::cout << "Largest double: " << largestDouble << std::endl;

return 0;
}

interlude

but I think it’s too late to add this feature, as long as it compiles without writing Concept, most programming won’t do the kind of stuff, and there are great bunch of legacy code in cxx. Besides, Rust and Swift comes with native Trait and Protocol and it doesn’t works only for Generic but prevasive in language, thus, there’s no overhead for understanding. Concept in cxx is like a bug fix or entending, that just bring more and more things to this language.

Now let’s refer to Python and type hinting,

1
2
3
4
5
from typing import Dict

def process_data(data: Dict[str, int]) -> None:
for key, value in data.items():
print(key, value)

but how many programers are using this feature? Just like for JavaScript and TypeScript, some times you must get off the legacy bundle and start somes from scratch.

Trait & Protocol

I will just list some usages of Trait Rust, as there’s a great deal of contents. And for swift, even more, as swift can be called as a Protocol Oriented Language

Define a trait, Define Protocol

1
2
3
pub trait Summary {
fn summarize(&self) -> String;
}

swift protocol and also define protocol access requirements. Remember properity with {get} or {get set} aren’t necessarily computed properity.

1
2
3
4
5
6
7
protocol SomeProtocol {
var mustBeSettable: Int { get set }
var doesNotNeedToBeSettable: Int { get }
static func someTypeMethod()
func random() -> Double
mutating func toggle()
}

implementation and default implementation, conditional implementation

1
2
3
4
5
impl Summary for NewsArticle {
fn summarize(&self) -> String {
// ...
}
}

swift methods are normally defined inside struct, but can also be extended outside with extension

1
2
3
struct someStrust: SomeProtocol, AnotherProtocol {
// confroms must be explicate, except for default implementation and conditional implementaton
}

it’s not inheritance, as still need to explicatedly implement for each struct rather than have methods and properity inherted from it’s parent.

with generics

as for code before, < operate must be explicatly available, thus you write code like this:

1
2
3
4
5
6
7
8
9
pub fn notify(item: &impl Summary) {
println!("Breaking news! {}", item.summarize());
}
pub fn notify<T: Summary>(item: &T) {
println!("Breaking news! {}", item.summarize());
}
pub fn notify<T>(item: &T) where T: Summary {
println!("Breaking news! {}", item.summarize());
}
1
2
3
4
5
6
protocol Comparable {
static func < (_ lhs: Self, _ rhs: Self) -> Bool
}
public func largest<T>(items: [T]) where T: Comparable {
/// implemenation
}

use as return type

1
2
3
fn returns_summarizable(switch: bool) -> impl Summary {
// implementation
}
1
2
3
func getLargest<T>(from items: [T] ) -> some T where T: Comparable {
// implementation
}

combination

1
2
3
impl<T: Display + PartialOrd> Pair<T> {
// implementation
}
1
2
3
protocol combinedProtocol: SomeProtocol, AnotherProtocol  {
// and you can add some additional requirement
}

Protocols can be inherated, which is infact combine and addition, but be carefully, as there can be problems like implementation conflictions. Anyway, compiler would get you notified.

and combines

1
2
func wishHappyBirthday(to celebrator: Named & Aged)
// Named and Aged are two Protocols

Generic and Lifetime in Rust

I haven’t got an soild understanding on this part.

Tests

I don’t know the most common way of doing test in cxx, make with cmake ctest.

In Rust, you test with and module marked #[cfg(test)], and in swift, you test with XCTest framework. XCTest I believe stands for Xcode Test, it does work without Xcode but got really terrible output sometimes.

Funtional Programming

I didn’t tell Procedural Programming and Functional Programming for a long time. 😣.

I first learn about Function Programming when learning python from CS61A with .map and .reduce.

Closures

Rust

1
2
3
4
fn  add_one_v1   (x: u32) -> u32 { x + 1 }
let add_one_v2 = |x: u32| -> u32 { x + 1 };
let add_one_v3 = |x| { x + 1 };
let add_one_v4 = |x| x + 1 ;

Swift

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{ (<#parameters#>) -> <#return type#> in
<#statements#>
}
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})

reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 } )

reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )

reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )

reversedNames = names.sorted(by: { $0 > $1 } )

reversedNames = names.sorted(by: >)

reversedNames = names.sorted() { $0 > $1 }

the last one is Trailing closures which is really handy, espically with @resultBuilder, in swift, you can explictly capture value by syntax.

1
someFunctionWithEscapingClosure { [self] in x = 100 }

and the type of closure is clear

1
let x: (Int, Int) -> Bool = {$0 > $1}

it’s handy to write type for a function as input and pass in a closure.

lambda in c++

1
2
3
[capture-list](parameters) -> return-type {
// Function body
}

it also get closure like in c++, what not as easy to write and use.

Capture value

In rust, you need to worry about ownership and reference. In swift, closure it self is reference type, we haven’t yet talked about class and ARC in swift, but closure can cause problems with ARC.

both in swift and rust, functional programming codes looks simlar, while one comes with deep and shallow copy, anothor with fine grained control on reference. Funtional programming makes code concise and clear, and as swift and rust are both static typed, compiling programming, thanks to the developement of compiler in these years, the performance is guaranteed.

Smart Pointer, and ARC, RAII

class in swift

We haven’t introduced class in swift. There are languages that have struct only, and langauage have class only. In C++, you get both struct and class, but the only difference is the access level, I think it’s primarly for working with c and is infact some kind’s reduantly.

In swift, class has some core difference with struct, struct is value type in swift, class is reference type, and class have deinit, inhertance.

swift 5.9

swift 5.9 introduced c++ interoprablity, this changed that struct can’t have deinit.

you can now give a struct deinit by let comform to :~Copyable this maybe be familar to c++ writters.

smart pointer

Both in rust and c++, you got smart pointers, and weak for reference cycle and break reference cycles.

RAII and ARC

In swift, you rarely use pointer directly as it’s unsafe, reference works with class and is implicate, but the class keyword should make you aware of it. I think it’s a gate that one start to understand swift. But ARC sometimes comes to problems with reference cycle as well and such program is easily created when using closure that captures class’s self.

Concurrency Support

Rust’s features on ownership and it’s integration with compiler’s check system.
And when it’s talking about Concurrency, Go is famous for it, I personally haven’t learn that language, but hear of it for it’s support for concurrency.

Rust’s Ownership module is the fundation of it’s concurrency support, and the left part to of the support isn’t in the language it self but in crates.

Swift on the other side bring’s some keyword for it.

C++ is a powerfully language that allows you to do many fine-grained control, I don’t if there’s language syntax level control on this, but I know many work you that you do with concurrency in C++ is using some specific libraries.

Object Oriented Programming

If a language must have inheritance to be an object-oriented language, then Rust is not one. The Rust programming langugage.

In swift, struct don’t directly have Inheritance by defination.

But wait, when we are talking about inheritance, as I remember learn Java, inheritance is something not easy to gasp and prone to make mistake, and do we really need them.

Protocol-Oriented Programming

Protocol-Oriented is as some level similar to Oriented Programming.

  • Post title:Rust, Swift and C++
  • Post author:Meqt
  • Create time:2023-06-07 13:49:42
  • Post link:https://meqtmac.github.io/2023/06/07/RustVsSwift/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.