Slide 1

Slide 1 text

@terhechte

Slide 2

Slide 2 text

@terhechte ABOUT ME Benedikt Terhechte @terhechte www.appventure.me iOS / macOS / Linux Developer

Slide 3

Slide 3 text

@terhechte • German Social Network • Based in Hamburg • 15 Mio Users • Native Apps on all platforms

Slide 4

Slide 4 text

@terhechte

Slide 5

Slide 5 text

@terhechte 3 NATIVE PLATFORMS • Implement everything three times • Amount of bugs X 3 • Alignment Overhead iOS, ANDROID, WINDOWS

Slide 6

Slide 6 text

@terhechte FIND WAYS OF SHARING CODE • React Native • C# • Flutter • Model Layer in C++

Slide 7

Slide 7 text

@terhechte USE NATIVE LANGUAGE FOR UI* • Kotlin (or Java) for Android • Swift (or Objective-C) for iOS • C# (or C++) for Windows * For Complex Projects

Slide 8

Slide 8 text

@terhechte USE C++ FOR SHARED MODEL • Facebook • Spotify • SoundCloud • PSPDFKit • …

Slide 9

Slide 9 text

@terhechte WHY NOT C++?

Slide 10

Slide 10 text

@terhechte IMAGES.GOOGLE.COM C++ MEME

Slide 11

Slide 11 text

@terhechte

Slide 12

Slide 12 text

@terhechte

Slide 13

Slide 13 text

@terhechte

Slide 14

Slide 14 text

@terhechte

Slide 15

Slide 15 text

@terhechte

Slide 16

Slide 16 text

@terhechte

Slide 17

Slide 17 text

@terhechte C++ IS NOT BAD • Very complex language • Tricky finding good developers • Easy to introduce serious bugs

Slide 18

Slide 18 text

@terhechte WHY NOT C++?

Slide 19

Slide 19 text

@terhechte Why not Kotlin Native?

Slide 20

Slide 20 text

@terhechte WHAT DOES C++ OFFER? • Build for cross Platform Development • Very High Speed • Huge Standard Library

Slide 21

Slide 21 text

@terhechte • Still Young • Many Performance Optimizations yet to come • Many Packages are still JVM only • SDKs (i.e. Dropbox, Firebase) often don’t exist for Kotlin Native yet KOTLIN NATIVE

Slide 22

Slide 22 text

@terhechte WHAT ABOUT RUST?

Slide 23

Slide 23 text

@terhechte QUICK PERFORMANCE COMPARISON

Slide 24

Slide 24 text

@terhechte A SUM OF STRINGS < 100 “54,28,42,77,34 ,90 , 8 , 120,76 , 52, 117 ,21 ,5” SPLIT, TRIM, TO INT, FILTER, SUM

Slide 25

Slide 25 text

@terhechte fun sumSplit(contents: String): Int { return contents.split(",").asSequence() .map({ str -> str.trim().toIntOrNull() }) .filterNotNull() .filter({ number -> number < 100 }) .fold(0, { a, b -> a + b}) } KOTLIN

Slide 26

Slide 26 text

@terhechte fn sum_and_split(contents: String) -> i32 { contents.split(",") .map(|s| { s.trim() }) .filter_map(|s| { s.parse().ok() }) .filter(|number: &i32| { *number < 100 }) .fold(0, |acc, x| acc + x) } RUST

Slide 27

Slide 27 text

@terhechte // Lifted from StackOverflow std::vector split(const std::string &s, char delim) { std::stringstream ss(s); std::string item; std::vector elems; while (std::getline(ss, item, delim)) { elems.push_back(std::move(item)); } return elems; } // Lifted from StackOverflow inline std::string trim(const std::string &s) { auto wsfront=std::find_if_not(s.begin(),s.end(),[](int c){return std::isspace(c);}); return std::string(wsfront,std::find_if_not(s.rbegin(),std::string::const_reverse_iterator(wsfront),[](int c){return std::isspace(c);}).base()); } inline std::optional operate(const std::string &s) { auto trimmed = trim(s); try { auto number = std::stoi(trimmed); return number; } catch (int ) { return {}; } } int test(std::string str) { // I found it tricky to use a fully functional C++ version without external libraries such as // boost. // So, instead, enjoy this half-hearted attempt auto contents = split(str, ','); std::vector> converted; converted.resize(contents.size()); std::transform(contents.begin(), contents.end(), converted.begin(), operate); auto sum = 0; for (std::optional n: converted) { if (n.has_value() ) { if (n.value() < 100) { sum += n.value(); } } } return sum; } C++

Slide 28

Slide 28 text

@terhechte 0s 3,5s 7s 10,5s 14s Rust C++ Kotlin 12,52s 0,6s 0,31s Compile Strings Functional

Slide 29

Slide 29 text

@terhechte 0s 0,5s 1s 1,5s 2s Rust C++ Kotlin 1,82s 0,1s 0,07s Run Strings Functional

Slide 30

Slide 30 text

@terhechte RUST IS CRAZY FAST (C++ IS FASTER)

Slide 31

Slide 31 text

@terhechte

Slide 32

Slide 32 text

@terhechte WHAT IS RUST • Started 2006 • Strongly Typed, Compiled • LLVM Based • Driven by Mozilla

Slide 33

Slide 33 text

@terhechte Mascot: Ferris

Slide 34

Slide 34 text

@terhechte –Wikipedia “Rust is intended to be a language for highly concurrent and highly safe systems.”

Slide 35

Slide 35 text

@terhechte SOME PROJECTS USING IT • Firefox (Rendering, CSS) • Dropbox (Custom Server Filesystem) • Oracle (Container Runtime) • Yelp, CloudFlare, …

Slide 36

Slide 36 text

@terhechte CROSS PLATFORM • Is not at home on any platform • Firefox is using it. Firefox is per definition cross platform • Rust & Packages run equally well on Windows, Linux, Android, iOS, macOS, etc

Slide 37

Slide 37 text

@terhechte ARCHITECTURES x86, x86_64, i386, ARM, ARM64, WASM, MIPS, MIPS64, SPARC, RISC, 16bit MSP430, etc OPERATING SYSTEMS Windows, macOS, Linux, iOS, Android, FreeBSD, NetBSD, Haiku, Redox, Haiku, etc TARGETS WebAssembly, Ruby, Python, JVM, C, C++, more

Slide 38

Slide 38 text

@terhechte

Slide 39

Slide 39 text

@terhechte let vector = vec![1, 2, 3, 4]; for element in vector { println!("The number is: {}", element); } vector.iter() .for_each(|chr| println!("A Char: {}", chr) ); ARRAYS [Vectors]

Slide 40

Slide 40 text

@terhechte let mut vector: Vec = Vec::new(); vector.push(5); MUTABILITY

Slide 41

Slide 41 text

@terhechte let mut string = "hey! 檀れい"; string.make_ascii_uppercase(); assert!(string == "HEY! 檀れい"); MUTABILITY

Slide 42

Slide 42 text

@terhechte fn add_two(first: i32, second: i32) -> i32 { return first + second; } FUNCTIONS

Slide 43

Slide 43 text

@terhechte struct User { name: String, age: i32 } impl User { fn formatted(&self) -> String { return format!(“{} {}", self.name, self.age); } } STRUCT METHODS

Slide 44

Slide 44 text

@terhechte enum RGB { Red, Green, Blue } enum Expression { Const(i32), Sum(i32, i32), Text(String) } SEALED CLASSES / ENUMS [Enums]

Slide 45

Slide 45 text

@terhechte struct Container { value: T } let contained = Container { value: 15 } GENERICS

Slide 46

Slide 46 text

@terhechte fn add_anything>(a: N, b: N) -> N { return a + b; } GENERIC CONSTRAINTS

Slide 47

Slide 47 text

@terhechte trait Countable { fn counted() -> i32; } INTERFACES

Slide 48

Slide 48 text

@terhechte trait Countable { fn counted() -> i32; } struct Number { number: i32 } impl Countable for Number { fn counted(&self) -> i32 { return self.number; } } INTERFACES

Slide 49

Slide 49 text

@terhechte • No Classes (But Reference Types and Interface Inheritance) • Limited Reflection (But Hygienic Macros) • No Function Overloading (But Operator Overloading)

Slide 50

Slide 50 text

@terhechte ! BENCHMARKS ARE THE DEVIL! Code Examples & Benchmarks

Slide 51

Slide 51 text

@terhechte JUST CURSORY BENCHMARKS • Mac mini 2018, 3.2GHz • Idiomatic code • Will release the benchmark suite on Twitter

Slide 52

Slide 52 text

@terhechte VERSIONS • Kotlin Native 1.0.3 (6. Dec 2018) • Rust 1.32.0 (16. Jan 2019) • C++ Clang 6.0

Slide 53

Slide 53 text

@terhechte KOTLIN: FUN RUST: FN

Slide 54

Slide 54 text

@terhechte FINDING PRIME NUMBERS And awful solution using functional programming

Slide 55

Slide 55 text

@terhechte fun isPrime(number: Int): Boolean { return (num > 1) && !((2 until num) .any({ n -> num % n == 0 })) } fn is_prime(num: i32) -> bool { num > 1 && !(2..num) .any(|n| num % n == 0) } PRIME NUMBERS

Slide 56

Slide 56 text

@terhechte 0s 3s 6s 9s 12s Rust C++ Kotlin 11,26s 0,35s 0,53s Compile Primes

Slide 57

Slide 57 text

@terhechte 0s 25s 50s 75s 100s Rust C++ Kotlin 90,39s 0,85s 0,85s Run Primes

Slide 58

Slide 58 text

@terhechte CHUNKS OF NUMBERS

Slide 59

Slide 59 text

@terhechte fun resize_chunk(chunk: List, scale: Int): List { return chunk.chunked(scale) .map({ innerChunk -> innerChunk.sum() }) } fun resize_image(image: List, width: Int, scale: Int): List { return image.chunked(width) .map({ innerChunk -> resize_chunk(innerChunk, scale) }) .flatten() } CHUNKS flatten(chunked > map > resize_chunk > chunked > map > sum)

Slide 60

Slide 60 text

@terhechte fn resize_chunk<'a>(chunk: &'a [usize], scale: usize) -> impl Iterator + 'a { chunk.chunks(scale) .map(|chunk| { chunk.iter().sum::() }) } fn resize_image(image: &[usize], width: usize, scale: usize) -> Vec { image.chunks(width) .map(|slice| resize_chunk(slice, scale)) .flatten() .collect() } CHUNKS flatten(chunks > map > resize_chunk > chunk > map > sum)

Slide 61

Slide 61 text

@terhechte uint64_t* resize_image(uint64_t* image, uint64_t size, uint64_t width, uint64_t scale, uint64_t* rsize) { uint64_t *result = malloc(sizeof(uint64_t) * (size / scale)); uint64_t pos = 0; for (uint64_t i=0; i

Slide 62

Slide 62 text

@terhechte 0s 3,5s 7s 10,5s 14s Rust C Kotlin 13,92s 0,12s 0,67s Compile Chunks Imperative

Slide 63

Slide 63 text

@terhechte 0s 40s 80s 120s 160s Rust C Kotlin 143,76s 0,67s 0,76s Run Chunks Imperative

Slide 64

Slide 64 text

@terhechte fun resize_image(image: IntArray, width: Int, scale: Int): IntArray { val result = IntArray(image.size / scale) var pos = 0 for (i in 0 until image.size step width) { for (i2 in i until (i + width) step scale) { var sum = 0 for (i3 in i2 until (i2 + scale)) { sum += image[i3] } result[pos] = sum pos += 1 } } return result } CHUNKS

Slide 65

Slide 65 text

@terhechte 0s 1,5s 3s 4,5s 6s Rust C Kotlin 5,76s 0,67s 0,76s Run Chunks Imperative

Slide 66

Slide 66 text

@terhechte JSON Parse JSON into an array of structs

Slide 67

Slide 67 text

@terhechte #[derive(Deserialize)] struct Media { author: User, likes: Vec, comments: Vec, images: HashMap, description: String } let media: Vec = serde_json::from_str(contents)?; RUST

Slide 68

Slide 68 text

@terhechte @Serializable data class Media( val author: User, val likes: Array, val comments: Array, val images: Map, val description: String) val obj = Json.parse(Media.serializer().list, contents) KOTLIN

Slide 69

Slide 69 text

@terhechte 0s 4s 8s 12s 16s Rust Kotlin 15,82s 0,17s 27MB JSON into Structs

Slide 70

Slide 70 text

@terhechte 0MB 100MB 200MB 300MB 400MB Rust Kotlin 331MB 6MB Average Memory

Slide 71

Slide 71 text

@terhechte • Very similar Code • Much faster • Less Memory

Slide 72

Slide 72 text

@terhechte DEMO

Slide 73

Slide 73 text

@terhechte WHAT? • Focus on Model Layer • Kotlin is much better than Rust at UI Code • Swift is much better than Rust at UI Code

Slide 74

Slide 74 text

@terhechte HOW? • For iOS: Compile Rust into C library. Use via Objective-C Bridge. • For Android: Compile Rust via Swig

Slide 75

Slide 75 text

@terhechte DEMO

Slide 76

Slide 76 text

@terhechte

Slide 77

Slide 77 text

@terhechte CONCURRENCY FEARLESS* CONCURRENCY * Honestly, that’s what the Rust people call it

Slide 78

Slide 78 text

@terhechte WHY? • Multicore devices are ever more common • Especially in the model, you want to use all the cores as best as possible • Concurrency bugs (Data Races) are really easy most languages

Slide 79

Slide 79 text

@terhechte let mut data = vec![1, 2, 3]; for i in 0..100 { thread::spawn(|| { data.push(i); }); } RUST: to force the closure to take ownership of `data` use the `move` keyword

Slide 80

Slide 80 text

@terhechte let mut data = vec![1, 2, 3]; for i in 0..100 { thread::spawn(move || { data.push(i); }); } RUST: capture of moved value `data`

Slide 81

Slide 81 text

@terhechte let mut data = Arc::new(vec![1, 2, 3]); for i in 0..100 { let data_ref = data.clone(); thread::spawn(move || { data_ref.push(i); }); } RUST: cannot borrow data_ref as mutable. Arc is immutable

Slide 82

Slide 82 text

@terhechte let mut data = Arc::new(Mutex::new(vec![1, 2])); for i in 0..100 { let data_ref = data.clone(); thread::spawn(move || { let mut data = data_ref.lock.unwrap(); data_ref.push(i); }); } RUST: I Compile

Slide 83

Slide 83 text

@terhechte use rayon::prelude::*; fn sum_of_squares(input: &Vec) -> i32 { input.par_iter() // <-- just change that! .map(|&i| i * i) .sum() }

Slide 84

Slide 84 text

@terhechte • Rust is really powerful at multicore programming • It also becomes much easier to write multicore code • Ecosystem is constantly improving

Slide 85

Slide 85 text

@terhechte

Slide 86

Slide 86 text

@terhechte OTHER THINGS • Hygienic Macros with IDE support • Go-Like Channels in the standard library • Futures (async await) coming soon • SIMD Support • Limited support for using C++ libraries

Slide 87

Slide 87 text

@terhechte OTHER THINGS • Really fast web frameworks • Fantastic package manager • Very welcoming community

Slide 88

Slide 88 text

@terhechte UPSIDES • Easier & Safer than C++ • Much more similar to Kotlin than to C++ • Really Fast • Generated binaries are kinda small (starting from ~500kb)

Slide 89

Slide 89 text

@terhechte UPSIDES • Consumes very little memory • Excels at concurrency • Great, cross-platform ecosystem • Also share code with Ruby, Python, Backend etc

Slide 90

Slide 90 text

@terhechte DOWNSIDES • No classes, no easy shared mutable state • Initial learning phase is hard (Memory Management) • Difficult to hire but much easier to learn than C++ • Tooling for Android / iOS is especially young & verbose

Slide 91

Slide 91 text

@terhechte WHAT NOW?

Slide 92

Slide 92 text

@terhechte IF YOU’RE JUST PARSING JSON FROM THE SERVER, FOR THE LOVE OF GOD, DON’T USE RUST.

Slide 93

Slide 93 text

@terhechte WHERE IS C++ NEEDED • Complex projects with lots of on-device logic • PDF Parsing (PSPDFKit) • Cross Platform DRM Music Player (Spotify) • Office Suite, Image Editing, etc

Slide 94

Slide 94 text

@terhechte IF YOU NEED SPEED, CONCURRENCY, LOW MEMORY, & CROSS PLATFORM ABSTRACTIONS, THEN CONSIDER RUST INSTEAD OF C++

Slide 95

Slide 95 text

@terhechte POLITICS THE IOS GUYS WANT SWIFT THE ANDROID GUYS WANT KOTLIN RUST IS THE SAFE MIDDLE ;)

Slide 96

Slide 96 text

@terhechte Benedikt Terhechte - twitter.com/terhechte Podcast: contravariance.rocks Questions? I have 100 more slides XING is HIRING IN GERMANY, SPAIN, AND PORTUGAL