├── LICENSE └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2018, don bright 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | [![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-direct.svg)](https://stand-with-ukraine.pp.ua) 4 | 5 | 6 | ## Singularity notice 7 | 8 | As of 2023, 90+% of the stuff on this cheat-sheet you can figure out a lot faster by asking an AI, like ChatGPT. Ask it simply: 9 | 10 | "Please help me write a Rust function that takes a Vector of floating point numbers and returns an array of strings" 11 | 12 | The LLM will spit out example code in two seconds, and then within three clicks you can copy/paste that code into 13 | playground.rust-lang.org , hold down Control-Enter, and run it, and if there is an error, just copy/paste the entire 14 | error message back into ChatGPT and it will tell you what the error was and how to fix it. You can easily adapt this 15 | to your own needs much faster than building up some example from a cheatsheet. 16 | 17 | Now if you want to learn more about something, its really easy to just ask the LLM 18 | 19 | "Could you please explain to me how Vectors and Strings in Rust work? What are they? Explain it to 20 | me like im 10 years old, then again like I'm an undergrad with a background in C++" 21 | 22 | and it will just write several pages of explanation and simple examples just for you. This kind of "Cheat Sheet" 23 | is kind of becoming obsolete in the face of such technology. 24 | 25 | ## Warning 26 | 27 | This cheat sheet is in a reasonably useful state for basic things, but it does contain many errors and typos and 28 | pedagological mistakes of omission and ordering & etc. 29 | 30 | Also note that Rust is still changing quite a bit in 2019-2022 so some of the below may be outdated/deprecated. 31 | 32 | ## Rust in a Nutshell 33 | 34 | * Syntax tokens somewhat similar to C / C++ / Go 35 | * Ownership of memory enforced at build time 36 | * Statically linked 37 | * Not so Object-Orientish, tends to be Functional-ish 38 | * Control flow using pattern matching, Option+Result enums 39 | * Packages: 'cargo add' command, https://crates.io 40 | * Testing: 'cargo test' command, #[test] unit tests, integration tests 41 | * Concurrency: ownership, mutability, channels, mutex, crossbeam + Rayon packages 42 | * Auto formatter: 'rustfmt filename.rs' (see rust-lang.org for installation) 43 | * compiler engine: LLVM, no non-LLVM compilers yet 44 | * To use raw pointers, low level, call C/C++: unsafe{} keyword + ffi package 45 | * smart pointers and reference counted: Box, Rc, Arc 46 | * online playgrounds: https://play.rust-lang.org, https://tio.run/ 47 | * A survivial horror game where griefing is ... oops wrong Rust 48 | * Polymorphism: Traits, Trait Extensions, Trait Objects 49 | * Generic programming (a la C++ Templates): Generic Types 50 | 51 | ## Hello World 52 | 53 | See https://www.rust-lang.org for installation details. 54 | 55 | ```rust 56 | fn main() { 57 | println!("Hello World"); 58 | } 59 | ``` 60 | 61 | ```bash 62 | $ rustc main.rs 63 | $ ./main 64 | Hello World 65 | ``` 66 | 67 | ## Hello Packages, Hello Tests, Hello Dependencies 68 | 69 | ```bash 70 | $ rustup.sh # install rust, see rust-lang.org for details 71 | $ cargo new myproj # start new executable project under myproj path 72 | $ cd myproj # cd into the new directory 73 | $ ls -lR # list our skeleton of files 74 | src/main.rs # main.rs, has main() entry point 75 | Cargo.toml # Cargo.toml defines packaging 76 | $ $EDITOR Cargo.toml # add dependencies and other details 77 | [package] 78 | name = "helloworld" 79 | version = "0.1.0" 80 | authors = ["ada astra "] 81 | 82 | [dependencies] 83 | serde = "1.0.80" 84 | # edit main.rs to say "extern crate serde;" and "use serde::*;" 85 | $ cargo add chrono # auto add dependency w/o wediting Cargo.toml 86 | # edit main.rs to say "extern crate chrono;" and "use chrono::*;" 87 | $ cargo build # downloads dependencies + builds main.rs 88 | $ cargo run # runs program created from main.rs 89 | $ cargo test # runs tests (in parallel by default) 90 | $ cargo test -- --test-threads=1 # run tests one at a time 91 | $ cargo test -- --nocapture # run tests, show output 92 | $ cargo run --example fundemo -- --arg # run example with arg (./examples subdir) 93 | $ cargo build --feature blargh # build, enable the blargh feature of the crate 94 | ``` 95 | 96 | ## Mutability basics 97 | 98 | ```rust 99 | let x = false; // all variable bindings are immutable by default 100 | x = true; // err // compile error. can't change a variable which has an immutable binding 101 | let mut p = false; // "mut" designates a binding as mutable 102 | p = true; // ok, a variable with mutable binding can change; 103 | ``` 104 | 105 | ## types, variables, declarations, initialization 106 | ```rust 107 | let x: bool = false; // let keyword 108 | let k = false; // rustc can determine some types automatically 109 | let y: char = '上'; // all chars are 4 bytes 110 | let 上 = 5; //err // error. identifiers must be ASCII characters 111 | let a: i8 = -2; // 8 bit signed integers, also i16, i32, i64 112 | let b: u8 = 200; // 8 bit unsigned integers, also u16, u32, u64 113 | let n: f32 = 0.45; // 32 bit float (automatcally converted+rounded from base-10 decimal to binary) 114 | let n2 = 42.01f64; // 64 bit float literal of the number 42.01 (approximately) 115 | let r: [u8;3] = [3,4,5]; // array of 3 int, immutable, cannot grow or change values 116 | let mut rm: [u8;3] = [3,4,5]; // same as r but mutable. cannot grow, but values can change. 117 | let mut s1 = [0;500]; // array of 500 integers, each initialized to 0. 118 | s1[0..200].fill(7); // set the first 200 integers to the value 7 119 | s1[400..].fill(5); // set the last 100 integers to the value 5 120 | let s2 = &r[0..2]; // slice of array, s==&[3,4] 121 | let s3 = &r[0..2][0]; // index into slice, s==3 122 | let s4 = &r[1..]; // slice from index 1 to end 123 | let s5 = &r[..2]; // slice from beginning to index 2 124 | let mut u:Vec = Vec::new(); // create empty vector of unsigned 8 bit int, can grow 125 | let mut v = vec![3,4,5]; // initialize mutable vector using vec! macro 126 | let w = vec![1,12,13]; // vectors can be immutable too 127 | let z = (0..999).collect::>(); // init Vector from Range (0..999) 128 | u.push( 2 ); // add item to vector 129 | v.rotate_right( 1 ); // in-place rotate of mutable vector [5,3,4] 130 | v.rotate_left( 1 ); // in-place rotate [3,4,5] 131 | let s6 = &mut v[1..]; // mutable slice, allows vector-like operations on a... 132 | s6.rotate_right( 1 ); // ...subslice of the vector [3,5,4] 133 | u.pop(); // vectors can pop, return+remove last input (like a stack) 134 | v.contains(&3); // true if vector contains value 135 | v.remove(1); // remove the nth item from a vector... 136 | v.append(u); // append v with u (u becomes empty ([]), both mutable) 137 | v.extend(w); // extend v with w (v owns w, w can be immutable) 138 | v.resize(200,0); // make vector have 200 elements, set them to 0 139 | v.resize(4398046511104,0); // memory allocation of 4398046511104 bytes failed. core dumped. 140 | // There is no malloc() style checking for success, Rust just crashes 141 | v[0..100].fill(7); // set the first 100 elements in v to the value 7 142 | v[150..].fill(9); // set the last 50 elements in v to the value 7 143 | v.fill(9); // set all elements of v to have the value 9 144 | let x = &w[1..]; // get a slice of a vector (a view into it's elements) 145 | print("{:?}",x); // [12,13]; 146 | let vs = v.len(); // length of vector 147 | let (p,d,q) = (4,5,6); // tuple() can assign multiple variables at once 148 | print("{}",p); // you can use them alone after tuple assignment 149 | let m = (4,5,"a"); // tuples can have multiple different types as elements 150 | let (a,b) = m.1, m.3; // tuple dereference with .1, .2, .3 151 | let (c,d) = m.p, m.q; //err // error, cannot index into a tuple using a variable 152 | 153 | let s = String::from("上善若水 "); // String is a heap variable. Strings are UTF8 encoded. 154 | let s2 = "水善利萬物而不爭"; // "" literals are type &str, different from String 155 | let s3 = &s; // & when prefixed to String gives &str 156 | let s4 = s2.to_string(); // create String from &str 157 | let s5 = format!("{}{}",s2,s3); // concatenate &str to &str 158 | let s6 = s + s2; // concatenate String to &str 159 | for i in "말 한마디에 천냥 빚을 갚는다".split(" ") {print!("{i}");} // split &str 160 | s.chars().nth(4); // get nth char. 161 | s.get(2..).unwrap(); // ERROR // get substring failed because 上 was 3 bytes 162 | s.get(3..).unwrap(); // 善若水 163 | s.trim(); // 上善若水, no trailing space 164 | s.starts_with("上善"); // true 165 | s.ends_with("水"); // true 166 | 167 | let i4 = s.find('水').unwrap_or(-1); // index of character (not always a byte offset, b/c utf8) 168 | let hellomsg = r###" // Multi-line &str with embedded quotes 169 | "Hello" in Chinese is 你好 ('Ni Hao') 170 | "Hello" in Hindi is नमस्ते ('Namaste') 171 | "###; 172 | 173 | let x = vec![5,12,13]; // indices into slices/arrays/vectors must be of type usize 174 | let y = 2 as u8; // using u8, u16, etc will not work 175 | print!("{}",x[y]); // error[E0277]: the type `[{integer}]` cannot be indexed by `u8` 176 | let z = 2 as usize; // usize is the pointer size. used in loops, vector length, etc 177 | print!("{}",x[z]); // ok. there is also "isize" if usize needs to be signed 178 | 179 | const BILBOG: i32 = 10; // constant 180 | static ORGOG: &str = "zormpf"; // static, global-ish variable 181 | static FOOBY: i32 = 5; // statics are only mutable inside unsafe{} blocks. 182 | static Z_ERRMSG : [&str;2] = ["need input","need more input"]; // static strings 183 | 184 | type Valid = bool; // typedef ( make your own type names ) 185 | 186 | let mut v = vec![1u8,2u8,3u8]; // determine the type of expression expr by looking at rustc error 187 | println!("{}",v.iter_mut()); // for example, if we want to know the type of v, build an error 188 | println!("{}",v.iter_mut()); // type of v.iter_mut() is std::slice::IterMut<'_, u8>` 189 | ``` 190 | 191 | 192 | ## Operators 193 | 194 | ```rust 195 | 1 + 2 - 3 * 4 / 5 // arithmetic add, minus, multiply, divide 196 | 7 % 5 // modulo (remainder) 197 | & | ^ // bitwise and, or, xor 198 | << >> // leftshift, rightshift, will crash on overflow 199 | // note that in C, overflowing << is actually undefined. 200 | // Rust has multiple versions, each defined. 201 | let a:u8 = 0b10110011; // print!("{:08b}",a); // 0b10110011 padded binary output 202 | a.rotate_left(1) // 01100111 circular bit rotation, out of left -> in at right 203 | a.wrapping_shl(1) // 01100110 this destroys the left-most bits that would cause overflow 204 | a.overflowing_shl(1)// 01100110,false returns tuple (value,did it overflow the number type) 205 | a.rotate_right(4) // 11011001 circular bit rotation to the right 206 | !a // 01001100 bitwise not 207 | a == b != c < d <= e > f >= g // logical comparison 208 | a && b || c ! d // logical boolean, and, or, not 209 | 210 | let a = 5; // pointer + dereference example 211 | let b = &a; // &a is 'address of a' in computers memory 212 | let c = *b; // *b is contents of memory at address in b (dereference) 213 | print("{c}"); // 5 214 | 215 | overloading: see struct 216 | ``` 217 | 218 | ## Run time errors, Crashing, panic, except, unwrap, Option, Result 219 | 220 | ```rust 221 | panic!("oops"); // panic!() instantly crashes program 222 | let v = vec![3,4,5]; 223 | let a = v[0]; // ok, normal lookup, a is 3 224 | let b = v[12]; // will call panic! at runtime, v[12] doesn't exist 225 | ``` 226 | 227 | ```bash 228 | $ export RUST_BACKTRACE=1 229 | $ cargo run # will tell you exact line where panic occured, with call stack trace 230 | ``` 231 | 232 | ### Option - a type for functions that may return Some thing, or None thing 233 | 234 | ```rust 235 | let v = vec![3,4,5]; // create Vector with three elements. then try to get() the element at index 12. 236 | let c = v.get(12); // there is no element at index 12, but this will not crash, get returns an Option 237 | print!("{:?}",v.get(12)); // prints the word "None", since there is no 12th element in v 238 | print!("{:?}",v.get(0)); // prints the word "Some(3)", because there is an element at index 0 239 | // Options have a value of either None, or Some(item), i.e. they are "Enums" 240 | let e = v.get(0).unwrap(); // ok, 'unwrap' the Option returned by get(0), e is now 3 241 | let d = v.get(12).unwrap(); // this crashes. 'unwrap' of a None Option will call panic! 242 | let f = v.get(5).unwrap_or(&0); // unwrap_or gives a value if get() is None. f = 0 243 | ``` 244 | Option and Match - a control flow similar to **if else** but with more error checking at compile time 245 | ``` 246 | let x = v.get(12); 247 | match x { Some(x)=>println!("OK! {x}"), // print OK if v has 13th item 248 | None=>println!("sad face"), } // otherwise print sad face 249 | // you will get a compile-time error if you forget to handle both Some and None 250 | ``` 251 | 252 | ### Result - a type like Option but with Ok() and Err() instead of Some() and None. 253 | 254 | ```rust 255 | 256 | match std::env::var("SHLVL") { // env::var() returns a std::result::Result() enum type. 257 | Ok(v)=>println!("SHLVL = {v:?}"), // if OK, std::env returns value of Environment variable 258 | Err(e)=>println!("error message: {:?}",e.to_string()) }; // if not, error message 259 | 260 | note there is also std::io::Result used a lot in file operations. 261 | 262 | ``` 263 | 264 | **If Let** - A control statemnt like match but with a no-op for None or Err() 265 | 266 | ```rust 267 | 268 | if let Some(x) = v.get(12) { println("OK! {x}"); } 269 | // we don't have to type out a handler for None. it does nothing. 270 | 271 | if let Ok(x) = std::env::var("SHLVL") { println!("SHLVL = {x:?}"); } 272 | // if the Result is Err, then nothing is done. 273 | ``` 274 | 275 | Option in particular can prevent the use of null pointers, preventing crashes one might see in C/C++. 276 | Say we are keeping tack of Owls, we know each Owls name, but not necessarily their last location. 277 | 278 | ```rust 279 | struct Owl { name: String, 280 | last_location: Option } // in C++ location might be a *char which could init as NULL 281 | 282 | let owls = [Owl{name:"Opv".to_string(),last_location:None}, 283 | Owl{name:"Marty".to_string(),last_location:Some("Barn".to_string())}]; 284 | 285 | for owl in &owls { 286 | match &owl.last_location { 287 | None=>println!("Owl named {} - location is not being tracked",owl.name), 288 | Some(loc)=>println!("Owl named {} - last known location was {}",owl.name,loc), 289 | }} 290 | // note that we did not have to check for null pointers, nor derefernece any 291 | // pointer. if we forgot to check for None, the compiler would give an error at compile time. 292 | // there are no null-related runtime errors possible. 293 | 294 | ``` 295 | 296 | Note that there are no Exceptions. Panic/Option/Result/multi-value-return are used instead. 297 | 298 | ## Printing 299 | 300 | ```rust 301 | println!("Hello, 你好, नमस्ते, Привет, ᎣᏏᏲ"); // unicode text is OK 302 | print!("Hi, is {}x{}={} ?",6,9,42); // curly braces {} get replaced by arguments 303 | Hi, is 7x9=42 ? 304 | let (meepo,beepo) = (5,6); // print variables without using arguments.. 305 | print!("{meepo}:{beepo}"); // .. by putting the names inside {} 306 | 307 | let v = vec![1,2,3]; 308 | println!( "v[0] from {:?} = {}", v, v[0] ) // {:?} can print lots of special types 309 | println!("{:02x?}",v); // {:02x?} can print 2 digit hex of vector 310 | let s = format!( "x coord={}", p.X ) // print to string 311 | s2 := fmt.Sprintf( "{e}", 17.0 ) // another way to print to string 312 | use std::fmt::Write; // yet another way - like C++ stringstream 313 | let mut s3 = String::new(); // String implements fmt::Write so we can 314 | match writeln!(s3,"Hello There") { // write to a String like a file. 315 | Ok(_)=>(), Err(e)=>println!("error writing to string {} {:?}",s3,e),} 316 | writeln!(s3,"Hello Too").unwrap_or(()); // the concise version w/o any error msg 317 | 318 | // C / printf style formatted output: 319 | println!("hex:{:x} bin:{:b} sci:{:e}",17,17,17.0); // hexadecimal, binary, etc. 320 | // "hex:11 bin:10001 sci:1.7e1" 321 | // Pad with zeros: 322 | println!("dec:{:#04} hex:{:#06x} bin:{:08b} sci:{:09e}",17,17,17,17.0); 323 | // "dec:0017 hex:0x0011 bin:00010001 sci:00001.7e1" 324 | println!(" {:.40} ", 1.0f32/3.0f32 ); // print 40 digits of precision for floating point 325 | // "0.3333333432674407958984375000000000000000" 326 | println!(" {:>4} {:>4} ", 232, 8 ); // pad as columns, width 4 spaces, align right 327 | // " 232 8" 328 | let mut s=String::new(); // build string, concatenate over lines 329 | s.push_str(&format!("{} {} ",1,2)); 330 | s.push_str(&format!("{} {}\n",3,4)); // "1 2 3 4\n" 331 | let mut s2=String::new(); // alternate version, same goal 332 | write!(s2,"{} {}",1,2).unwrap_or(()); 333 | writeln!(s2,"{} {}",3,4).unwrap_or(()); // "1 2 3 4\n" 334 | 335 | println!("\u{2766}"); // ❦ unicode floral heart, character hex 2766 336 | 337 | // derive Debug can make your own structs, enums, and unions printable by {:?} 338 | #[derive(Debug)] 339 | struct Wheel{radius:i8} 340 | println!("{:?}",vec![Wheel{radius:4},Wheel{radius:3},Wheel{radius:2}]); 341 | // [Wheel { radius: 4 }, Wheel { radius: 3 }, Wheel { radius: 2 }] 342 | 343 | // If you want to customize your debug output, you can implement Debug yourself 344 | use std::fmt; 345 | struct Wheel{radius:i8} 346 | impl std::fmt::Debug for Wheel { 347 | fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result{ 348 | write!(f, "輪:徑[{}]", self.radius) 349 | }} 350 | println!("{:?}",vec![Wheel{radius:4},Wheel{radius:3},Wheel{radius:2}]); 351 | // [輪:徑[4], 輪:徑[3], 輪:徑[2]] 352 | 353 | // fmt::Display makes your own structs and enums printable with ordinary {} symbol 354 | impl std::fmt::Display for Wheel{ 355 | fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result{ 356 | write!(f, "W[{}]", self.radius) 357 | } 358 | } 359 | 360 | // Display for enums 361 | pub enum Apple{PinkLady,HoneyCrisp} 362 | impl std::fmt::Display for Apple { 363 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 364 | match self { Apple::PinkLady=>write!(f, "Ap:PLad"), 365 | Apple::HoneyCrisp=>write!(f, "Ap:HonCr"), 366 | } 367 | } 368 | } 369 | ``` 370 | 371 | ### loop, for, while 372 | ```rust 373 | 374 | for i in 0..10 {print!("{},",x)}; // 0,1,2,3,4,5,6,7,8,9 375 | for i in 0..10.rev() {print!("{},",x)}; // 9,8,7,6,5,4,3,2,1,0 376 | for i in (0..10).step_by(2) {print!("{},",x)}; // 0 2 4 6 8 377 | for i in (0..10).skip(1).step_by(2) {print!("{},",x)}; // 1 3 5 7 9 378 | for i in (0..10).rev().step_by(2){print!("{},",x)}; // 9 7 5 3 1 379 | for i in (0..=10).rev().step_by(2){print!("{},",x)}; // 10 8 6 4 2 0 380 | for i in (0..=10).step_by(2){print!("{},",x)} ; // 0 2 4 6 8 10 381 | for i in (0..9).rev().step_by(2){print!("{},",x)} ; // 8 6 4 2 0 382 | for i in (0..9).step_by(2){print!("{},",x)} ; // 0 2 4 6 8 383 | for i in (0..10).cycle().skip(5).take(10){print!("{},",x)} // 5 6 7 8 9 0 1 2 3 4 384 | 385 | let v = vec![3,5,7]; 386 | for n in v { println!("{}",n) } // for loop over vector 387 | 388 | for (i, n) in v.iter().enumerate() { // iterate with (index, item) tuple 389 | println!("{},{} ", i, n);} // 0,3 \n 1,5 \n 2,7 390 | 391 | let mut i = 0; // while loop 392 | while i < 10 {print!("{}",i); i += 2;} // 0 2 4 6 8 393 | let mut j:u8 = 9; 394 | while j > 0 {print!("{}",j); j -= 2;} // 9 7 5 3 1 Panic! j is unsigned but goes below 0 395 | 396 | let mut i = 0; // loop 397 | loop { i=i+1; if i<10 { break; } }; // plain loop, exit with break; 398 | let x = loop { i=i+1; if i>=10 {break i;} } // loop that returns value, x = 10 399 | ``` 400 | 401 | Iterators as an alternative to loops 402 | 403 | ```rust 404 | let v = vec![3,5,7,11]; // vector to iterate 405 | v.iter().for_each(|x| print!("{} ",x)); // for_each over iterator, 3 5 7 11 406 | print!("{}",v.iter().fold(0,|a,i| a+i)); // adds 0+1+2+3, prints 6. 407 | // for more info , see iterators/functional section below 408 | ``` 409 | 410 | ```rust 411 | // While Let, can be used in situations where we expect Option::Some() for several iterations, 412 | // but we expect a Option::None() to be at the end of the iterations. For example: 413 | let mut x = (0..12).filter(|x| x%2==0); // x is an iterator 414 | while let Some(i) = x.next() {print!("{}:",i);} // While loop over the iterator 415 | // 0:2:4:6:8:10: // prints the even numbers between 0 and 12 416 | ``` 417 | 418 | ## Parallel processing 419 | 420 | ```rust 421 | extern crate rayon; 422 | use rayon::prelude::*; 423 | 424 | fn main() { 425 | let mut v = Vec::new(); // create a vector of floats, to multiply each by 0.9 426 | for i in 0..1024*1280 { v.push(i as f32); } 427 | v.iter_mut().for_each( |x| *x = *x * 0.9 ); // single thread version 428 | v.par_iter_mut().for_each( |x| *x = *x * 0.9 ); // multiple threads version 429 | 430 | let v = (0..999).collect::>(); // par_iter is slightly different 431 | let tot = v.par_iter().map(|i|i*i).reduce(||0,|a,x|a+x); // parallel sum of squares 432 | // see https://docs.rs/rayon/latest/rayon/iter/trait.ParallelIterator.html#method.fold 433 | // for info on fold, reduce, map, etc, in Rayon 434 | 435 | very_slow_function1(); // two single threaded functions that take a long time 436 | very_slow_function2(); 437 | 438 | rayon::join( || very_slow_function1() // run them in parallel if appropriate 439 | || very_slow_function2() ); 440 | 441 | s = "VeryLargeString ...pretend goes on for 10Mb "; // imagine 10Mb string 442 | s.chars().par_iter().do_stuff() // error // par_iter() cant work on string because 443 | // it doesnt know where to cut the byte boundaries 444 | let v = s.iter().collect::>9); // so convert to vector of char each 4 bytes 445 | v.par_iter().map(|ch| ch.to_ascii_lowercase()).do_stuff() // now you can 446 | 447 | } 448 | ``` 449 | 450 | ```bash 451 | $ cargo add rayon # add rayon dependency 452 | $ cargo run # installs rayon, runs program 453 | ``` 454 | 455 | ```rust 456 | channels: todo 457 | mutex: todo 458 | concurrency: todo 459 | ``` 460 | 461 | ## Smart pointers 462 | 463 | Box is a smart pointer. It is not possible to become null or to point to invalid memory. 464 | 465 | As an example, consider a tree data structure. In C you might have nodes that have 466 | pointers to child nodes, and then at the leaf nodes , the child node pointers are NULL. 467 | 468 | In Rust, the nodes have Boxed pointers to child nodes. The Boxes are all wrapped with an Option. 469 | So if there is no child, the Option is None. If there is a child, it is Some(Box(childnode)) 470 | 471 | ```rust 472 | struct Node { data: char, leftchild: Option>, rightchild: Option> } 473 | let mut x = Node{data:'x',leftchild:None,rightchild:None}; 474 | let y = Node{data:'y',leftchild:None,rightchild:None}; 475 | let z = Node{data:'z',leftchild:None,rightchild:None}; 476 | (x.leftchild,x.rightchild) = (Some(Box::new(y)),Some(Box::new(z))); 477 | // Node { data: 'x', 478 | // leftchild: Some(Node { data: 'y', leftchild: None, rightchild: None }), 479 | // rightchild: Some(Node { data: 'z', leftchild: None, rightchild: None }) } 480 | ``` 481 | 482 | Another way is to use Enum variants for the different node types, then you dont 483 | even need to use Option at all. Each node is actually a different Variant so the leaf 484 | nodes do not even have children they only have data, while the inner branch nodes have 485 | children but do not have data. 486 | 487 | See below section on "Enums - not like C" for more on how Enums work in Rust. 488 | 489 | ```rust 490 | enum Node { Branch(Box,Box), Leaf(char) } 491 | let y = Node::Leaf('y'); 492 | let z = Node::Leaf('z'); 493 | let x = Node::Branch(Box::new(y),Box::new(z)); 494 | // print!("{:?}",x); // Branch(Leaf('y'), Leaf('z')) 495 | ``` 496 | 497 | https://doc.rust-lang.org/std/boxed/index.html 498 | 499 | https://rosettacode.org/wiki/Huffman_coding#Rust 500 | 501 | Arc, RC - reference counted pointers. Todo 502 | 503 | ## Functions and closures 504 | ```rust 505 | fn add( a:i8, b:i8 ) -> i32 { b + a } // 'return' keyword optional 506 | fn getcodes(a:i8)->(i8,i32){ (9,a*99) } // multi return via tuples 507 | let (x, s) = getcodes( 3, 56 ); // assign multi-return w tuples 508 | fn multy(a:i8,b:i8=5)->i16{a*b} //error // Rust has no default parameters. 509 | fn multy(a:i8,b:Option)->i16 { // but you can fake it with Option unwrap_or 510 | a * b.unwrap_or(5) } // unwrap_or(x) means x is the default value 511 | fn main(){ print!("{}",multy(3,None));} // pass None to the func, result here=15 512 | fm main(){ print!("{}",multy(3,Some(4));} // awkward, tho, as normal calls require Some() 513 | fn f(t:i8) { // nesting functions is OK 514 | fn g(u:i8) { u*5 }; // g nested inside f 515 | let a = t + g(2); } // g can be called from inside f 516 | fn f2(t:i8) { // however, nested functs cannot access outside variables 517 | let mut m = 2; // m is declared outside the scope of g2 block 518 | fn g2(u:i8){u*5 + m};} // error[E0434]: can't capture dynamic environment in a fn item 519 | fn f(t:i8) { 520 | struct W{a:int}; t+1 } // structs can be declared inside functions too 521 | 522 | // function pointers 523 | fn addtwo(t:i8)->i8{t+2}; // simple function, adds 2 to argument. 524 | println!("{}",addtwo(5)); // prints 7 525 | let fp = addtwo; // fp = function pointer to addtwo function 526 | println!("{}",fp(5)); // now we can call fp() just like we called addtwo 527 | fn f(fp: F) where F: Fn(i8)->i8 { println!("{}",fp(1)) } 528 | // 'where F:Fn' lets us build a function that can accept another function as an argument 529 | f(fp); // call function f, passing a pointer to the 'addtwo' function. result=3 530 | 531 | type ZFillCallback = fn(bottom:u32,top:u32)->u32; // typedef of a function 532 | 533 | fn maximum(t:i8,...) {} // error, can't have variable number of arguments. 534 | // only macros! can be Variadic in Rust (see below) 535 | 536 | // closures 537 | let c = |x| x + 2; // define a closure, which is kinda like a lambda function 538 | let a = c(5); // closures can be called, like functions. result = 7 539 | let value = 5; // a closure can also read values outside its scope 540 | let d = |x| value + x;// d(n) will now add 'value' to any input n. (in this case,5) 541 | fn f(fp: F) where F: Fn(i8)->i8 { println!("{}",fp(1)) } // f takes a function as an argument 542 | f(c); // a closure can be passed, like a function pointer, result = 3 543 | f(|x| x * value); // and a closure can be anonymous, without a name. result = 5 544 | 545 | for i in 0..4.filter(|x| x>1) // anonymous closures are used often with iterators (see below) 546 | print!("{} ",i) // 2 3 (0 1 2 3 filtered to only values greater than 1) 547 | 548 | 549 | ``` 550 | 551 | ## Unit tests, integration tests 552 | 553 | Unit tests, placed in the same file as the code being tested 554 | 555 | ```rust 556 | ./src/lib.rs: 557 | 558 | pub fn process(v:&mut Vec)->&Vec{ v.update(|x| f(x)) } // main function called by users 559 | fn f(x:u8)->u8 { x*x } // small piece of our code, to test in unit testing 560 | 561 | #[cfg(test)] // cfg -> section will only compiled during 'cargo test' 562 | mod tests { // namespace helper 563 | use super::*; // bring in our functions above 564 | #[test] // next function will be a single test 565 | fn test_f() { assert!(f(4)==16); } // test f() by itself (unit) 566 | } 567 | ``` 568 | 569 | Integration tests, for overall crate, lives under ./tests/*.rs 570 | 571 | ```rust 572 | ./tests/file.rs: // will only be built dring 'cargo test' 573 | extern crate mypackage; // include package we are testing 574 | #test // treat next function as a test 575 | fn bigtest() { // not a unit test. instead, test overall code 576 | let mut d = vec![1,2,3]; // set up some typical data users would have 577 | let expected_results = vec![1,4,9]; // some results we expect 578 | assert!(process(d)==expected_results); // test what a user would typically call, process() 579 | } 580 | ``` 581 | 582 | ```bash 583 | $ cargo test # test build, will include cfg(test) sections 584 | -> test_f passed # cargo reports on passed tests 585 | -> test_bigtest failed # cargo reports on failed tests 586 | ``` 587 | 588 | ## Documentation 589 | 590 | rust-doc and cargo doc allow automatic building of html documentation 591 | for code. precede documentation of your code with three slashmarks 592 | instead of the normal two slashmarks, like so: 593 | 594 | ```rust 595 | /// blorg() returns the blorgification of input x 596 | /// # Details 597 | /// this code implements the krishnamurthi procedure 598 | /// for blimfication of zorgonautic primes 599 | /// # Arguments 600 | /// * `x` - typically a square number 601 | /// # Safety 602 | /// Cannot panic unless x overflows u64 603 | /// # Example 604 | /// let n = blorg(36); 605 | fn blorg(x:u64)->u64 { 606 | x+x*x 607 | } 608 | ``` 609 | 610 | Then run rust-doc or cargo doc and view the result. 611 | 612 | ```bash 613 | $ cargo doc 614 | $ firefox target/doc/cratename/index.html 615 | ``` 616 | 617 | Good examples of the results are on https://crates.io 618 | 619 | ## If, conditionals, patterns, match, control flow 620 | 621 | ```rust 622 | 623 | let oz = ounces_in_recipe(); 624 | 625 | // normal if else, like C, Pascal, etc 626 | if oz == 1 { 627 | print!("1 ounce") 628 | } else if oz == 8 { 629 | print!("1 cup") 630 | } else if oz == 16 { 631 | print!("1 pint") 632 | } else if oz == 128 { 633 | print!("1 gallon") 634 | } else { 635 | print!("non-unit # of ounces"); 636 | } 637 | 638 | match oz { // match, like case, works on literals not variables 639 | 1 => print!("1 ounce"), // "=>" signifies a branch or leg of the match 640 | 8 => print!("1 cup"), // have as many=> legs as you want 641 | 16 => print!("1 pint"), // end each leg with a comma , 642 | 128 => print!("1 gallon"), 643 | _ => print!("non-unit # of ounces") // underscore _ will match anything not previously matched 644 | } 645 | 646 | // match can work on enums too, if you want to limit what values a variable can be assigned 647 | pub enum UnitVolume{Cup,Pint,Gallon} 648 | 649 | let ozv = UnitVolume::Cup; 650 | 651 | match ozv { 652 | UnitVolume::Cup => print!("cup"), // "=>" signifies a branch or leg of the match 653 | UnitVolume::Pint => print!("pint"), // have as many=> legs as you want 654 | UnitVolume::Gallon => print!("gallon"), // end each leg with a comma , 655 | // don't need underscore match-arm with Enums, it knows its complete 656 | } 657 | 658 | let x = [1i8,2i8]; // match patterns can be structs, enums, arrays, etc 659 | let y = match x { // match can 'return a result' to y 660 | [1,0] => "1,0", // match a specific array 661 | [2,0]|[4,0] => "2,0 or 4,0", // |, binary or, can be used in patterns 662 | [_,2] => "ends with 2", // [_,2] will match [0,2] [1,2] [2,2], [3,2], etc 663 | _ => "other" 664 | }; 665 | println!("{}",y); // "ends with 2" 666 | 667 | 668 | let m = 3; // match patterns can only be constant, but you can 669 | let n = 4; // do similar things with "match guard", an if statement 670 | let y = match (n,m) { // which is inside of a => arm. First, we match tuple (n,m) 671 | (0,0) => "ok", // this leg matches any tuple with first element 0, return ok 672 | (3,_) if m%5==0 => "ok", // this leg matches when first element=3, and second divisible by 5 673 | (_,_) if m%3==0 => "ok", // this leg matches any tuple where the second element is divisble by 3 674 | _ => "stop", // this leg matches anything else. 675 | }; 676 | 677 | let hour = get_24hr_time(); // patterns can be integer ranges (x..=y) 678 | ampm = match hour { // however it has to be inclusive 679 | 0..=11 => "am" // 1..11 is not ok, 1..=11 is ok. 680 | 12..=23 => "pm" 681 | _=> "unknown" 682 | }; 683 | 684 | let x = 5i32; // we can use the match value in match arms, 685 | let y = match x-1 { // this is also called "binding with @", like so: 686 | 0..=9 => 7, // since x is 5, x-1 is 4 and 4 is in 0..=9, so y=7 687 | m @ 10..=19 => m*2, // if x-1 was 14, m becomes 14, so y would be 28 688 | _=> -1, // if x-1 was >=20, y would become -1 689 | }; 690 | 691 | let mut v = vec![0;4096]; // match also works with Result<> 692 | match File::open("/dev/random") { 693 | Ok(f)=>f.read(&v), 694 | Err(why)=>println!("file open failed, {}",why), 695 | } 696 | 697 | let v = vec![1,2,3]; // match works with Option too 698 | let n = 1; 699 | match v.get(n) { 700 | Some(x) => println!("nth item of v is {}",x), 701 | None => println!("v has no nth item for n={}",n), 702 | }; 703 | 704 | ``` 705 | 706 | See also: While let, if let. 707 | 708 | ## Ownership, Borrowing, References, Lifetimes 709 | 710 | Resources have exactly one owner. They can be 'moved' from one owner to another. 711 | 712 | ```rust 713 | // stack memory, no moves, only copies 714 | let a = 5; 715 | let b = a; // ok 716 | let c = a; // ok 717 | 718 | // heap memory 719 | let a = String::new(); 720 | let b = a; // 'move' of ownership from a to b 721 | let c = a; // error. cannot "move" a again, b already owns it 722 | 723 | // heap memory + function call 724 | let a = String::new(); 725 | let b = a; // 'move' of ownership from a to b 726 | fn f(t:String) { } // function takes ownership of the variable passed as t 727 | f(a); // error. f(a) would move a into f(), but a was already moved into b 728 | ``` 729 | 730 | Borrowing is an alternative to moving. It is done with References & (memory addresses) 731 | 732 | ```rust 733 | // heap memory, using borrows and references instead of moves 734 | let a = String::from("☪☯ॐ✡γ⚕✝"); 735 | let b = &a; // this is borrowing, not moving, a to b 736 | let c = &a; // it is OK to have more than one borrower of non-mutable variables 737 | println!("{}",a); // ☪☯ॐ✡γ⚕✝ 738 | println!("{:p}",&a); // 0x7ffcffb6b278 739 | println!("{:p}",b); // 0x7ffcffb6b278 // b and c hold the address of a 740 | println!("{:p}",c); // 0x7ffcffb6b278 741 | println!("{:p}",&b); // 0x7ffcffb6b290 // b and c are distinct variables 742 | println!("{:p}",&c); // 0x7ffcffb6b298 // with their own addresses 743 | ``` 744 | 745 | However borrowing has special rules regarding mutability. 746 | 747 | In a block, only one of these statements can be true for a given resource R 748 | * The program has one or more immutable references to R 749 | * The program has exactly one mutable reference to R 750 | 751 | ```rust 752 | 753 | // example error[E0502]: cannot borrow `a` as mutable because `a` is also borrowed as immutable 754 | let mut a = 5; 755 | let b = &a; 756 | let c = &mut a; 757 | 758 | // example error[E0499]: cannot borrow `a` as mutable more than once at a time 759 | let mut a = 5; 760 | let b = &mut a; 761 | let c = &mut a; 762 | 763 | ``` 764 | Another way to think about it is Shared vs Exclusive 765 | * A plain old & borrow is a Shared Reference, since multiple people can share at once 766 | * A &mut reference is an Exclusive reference, only one can have it. 767 | 768 | Lifetime in Rust: Resources are destroyed, (their heap memory is freed), at the end of a 'scope'. Their owners are also destroyed. That is the point of ownership - so that resources won't be accessed after they are destroyed, which is the source of a huge number of errors in C programs. 769 | 770 | Borrowed resources are not destroyed when the borrowed reference itself goes out of scope. However the borrow cannot "outlive" the destruction of the original resource nor it's owner. 771 | 772 | Take, for example, the case where we borrow a variable via ```&```. The borrow has a lifetime that is determined by where it is declared. As a result, the borrow is valid as long as it ends before the lender is destroyed. However, the scope of the borrow is determined by where the reference is used. 773 | 774 | ```rust 775 | fn main() { // main block starts 776 | let i = 3; // Lifetime for `i` starts. ────────────────┐ 777 | // │ 778 | { // new block starts │ 779 | let borrow1 = &i; // `borrow1` lifetime starts. ──┐│ 780 | // ││ 781 | println!("borrow1: {}", borrow1); // ││ 782 | } // block for `borrow1 ends. ─────────-──────────────┘│ 783 | // │ 784 | // │ 785 | { // new block starts │ 786 | let borrow2 = &i; // `borrow2` lifetime starts. ──┐│ 787 | // ││ 788 | println!("borrow2: {}", borrow2); // ││ 789 | } // block for `borrow2` ends. ───────────────────────┘│ 790 | // │ 791 | } // main block ends, so does Lifetime ends. ────────────┘ 792 | 793 | ``` 794 | 795 | 796 | ## Structs 797 | 798 | ```rust 799 | struct Wheel{ r:i8, s:i8}; // basic struct, like C, pascal, etc. r = radius, s = number of spokes 800 | struct badWheel{ mut r: i8, mut h: i8, }; // error, mut keyword doesnt work inside struct 801 | let w = Wheel{r:5,s:7}; // new wheel, radius 5, spokes 7, immutable binding 802 | w.r = 6; // error, cannot mutably borrow field of immutable binding 803 | let mut mw = Wheel{r:5,s:7}; // new mutable wheel. fields inherit mutability of struct; 804 | mw.r = 6; // ok 805 | 806 | impl Wheel { // impl -> implement methods for struct, kinda like a C++ class 807 | fn new(r: isize) -> Wheel { Wheel { r:r, s:4 } } // our own default 808 | fn dump( &self) { println!("{} {}",self.r,self.s); } // immutable self 809 | fn badgrow( &self) { self.s += 4; } // error, cannot mutably borrow field of immutable binding 810 | fn okgrow(&mut self) { self.s += 4; } // ok, mutable self 811 | }; 812 | w.dump(); // ok , w is immutable, self inside dump() is immutable 813 | w.okgrow(); // error, w is immutable, self inside okgrow() is mutable 814 | // cannot borrow immutable local variable `w` as mutable 815 | mw.dump(); // ok, mw is mutable, self inside dump is immutable. 816 | mw.okgrow(); // ok, mw is mutable, self inside grow() is mutable. 817 | 818 | #[derive(Default,Copy,Clone,Debug,PartialEq)] // automatic implementations 819 | struct Moo {x:u8,y:u8,z:u8,} 820 | let m1:Moo=Default::default(); // Default, x=0 y=0 z=0 821 | let m2:Moo=Moo{x:3,..Default::default()}; // Default, x=3 y=0 z=0 822 | let (mut n,mut k)=(M{x:-1,y:-1,z:-1},Default::default()); 823 | n=k; // Copy 824 | vec![M{x:0,y:1,z:2};42]; // Clone 825 | println!("{:?}",n); // Debug, formatter 826 | if n==k {println!("hi");}; // PartialEq, operator overloading 827 | 828 | // customized operator overloading 829 | impl PartialEq for Wheel{ fn eq(&self,o:&Wheel)->bool {self.r==o.r&&self.s==o.s} } 830 | if mw == w { print!("equal wheels"); } 831 | 832 | // default values for members of struct 833 | #[derive(Debug,Default)] 834 | struct Wheel1{ r:i8, s:i8}; 835 | println!("{:?}",Wheel1{..Default::default()}); // Wheel1 { r: 0, s: 0 } 836 | 837 | // custom default values for members of struct 838 | #[derive(Debug)] 839 | struct Wheel2{ r:i8, s:i8}; 840 | impl Default for Wheel2 { fn default() -> Wheel2{Wheel2 { r: 8, s: 8 }} } 841 | println!("{:?}",Wheel2{..Default::default()}); // Wheel2 { r: 8, s: 8 } 842 | println!("{:?}",Wheel2{r:10,..Default::default()}); // Wheel2 { r: 10, s: 8 } 843 | 844 | #[derive(Debug)] // Initialize one struct from another 845 | struct Apple {color:(u8,u8,u8),price:f32}; 846 | let a = Apple{color:(100,0,0),price:0.2}; 847 | let b = Apple{color:(9,12,38),..a }; // this is called "struct update" 848 | 849 | ``` 850 | 851 | Structs with function pointers as members 852 | 853 | ``` 854 | struct ThingDoer { 855 | name: String, 856 | do_thing: fn(x:&Vec) -> u8, 857 | } 858 | fn baseball(v:&Vec)->u8{ 42 } 859 | fn main (){ 860 | let adder = ThingDoer{name:"addthing".to_string(), do_thing:|n| n.iter().fold(0,|a,b|a+b)}; 861 | let multer = ThingDoer{name:"multhing".to_string(), do_thing:|n| n.iter().fold(1,|a,b|a*b)}; 862 | let nother = ThingDoer{name:"nothing".to_string(), do_thing:|n|baseball(&n)}; 863 | let (f1,f2,f3) = (adder.do_thing,multer.do_thing,nother.do_thing); 864 | let v = vec![1,2,3,4]; 865 | println!("{} {} {}",f1(&v),f2(&v),f3(&v)); 866 | } 867 | ``` 868 | 869 | 870 | ## Enums - not like C 871 | 872 | Enums in Rust do more than Enums in C/C++. They are actually more like 873 | Tagged Unions or ADT / Algebraic Data Types from Functional programming languages. 874 | Other places call them Sum Types 875 | 876 | ```rust 877 | enum Fruit { Apple, Banana, Pear } 878 | let x = call_some_function( Fruit::Apple ); 879 | enum Errs { ErrOK = 3, ErrFile = 5, ErrFire = 12 } // custom Discriminats (integers) 880 | enum Planet { Earth = 3, Mars, Jupiter } // mars will be 4, Jupiter 5. 881 | 882 | enum Blorg { // enums can have different types as members 883 | Flarg(u8), 884 | Blarg(u32), 885 | Norg(String), 886 | Florg(bool) 887 | } 888 | 889 | let x = Blorg::Flarg(1); // enums can be detected with a match 890 | match x { 891 | Blorg::Flarg(1) => println!("x is a Flarg with value of 1!"), 892 | Blorg::Flarg(_) => println!("x is a Flarg with non-1 value"), 893 | Blorg::Norg(_) => println!("x is a Norg"), 894 | _ => println!("neither Flarg nor Norg"), 895 | } 896 | 897 | // Enums can also derive traits 898 | #[derive(Clone,Debug,PartialEq)] // cannot derive Default on enum 899 | enum ColorMapData { 900 | OneByteColors(Vec), 901 | FourByteColors(Vec), 902 | } 903 | 904 | // Enums can also have methods / functions, using impl 905 | // the key is that inside the function, pattern matching is used 906 | // to determine which variant of the Enum the function receives 907 | impl ColorMapData { 908 | fn description(&self)->String { 909 | let (numcolors,numbytes) = match self { 910 | ColorMapData::OneByteColors(x) => (x.len(),"1"), 911 | ColorMapData::FourByteColors(x) => (x.len(),"4"), 912 | }; 913 | format!("ColorMap with {} colors, {} bytes per color",numcolors,numbytes) 914 | }} 915 | 916 | // functions can take Enum as arguments 917 | fn examinecm( c:ColorMapData ) { println!("{}",c.description()); } 918 | let ca = ColorMapData::FourByteColors(vec![0xFFAA32FFu32,0x00AA0011,0x0000AA00]); 919 | let cb = ColorMapData::OneByteColors(vec![0,1,3,9,16]); 920 | examinecm(ca); 921 | // ColorMap with 3 colors, 4 bytes per color 922 | examinecm(cb); 923 | // ColorMap with 5 colors, 1 bytes per color 924 | ``` 925 | 926 | Using strum, a reflection package, you can iterate an enum's values 927 | 928 | ``` 929 | $ cargo add strum strum_macros 930 | use strum::IntoEnumIterator; 931 | #[derive(Debug, strum_macros::EnumIter)] 932 | enum Tofu { Smooth, Firm, ExtraFirm } 933 | Tofu::iter().for_each(|d|print!("{d:?},")); // Smooth,Firm,ExtraFirm 934 | ``` 935 | 936 | ## Collections, Key-value pairs, Sets 937 | 938 | HashMap, aka associative array / key-value store / map 939 | 940 | ```rust 941 | use std::collections::{HashMap}; 942 | let mut m = HashMap::new(); 943 | m.insert('a', 1); // key is 'a', value is 1 944 | let b = m[&'a']; // [] lookup, this crashes at runtime if 'a' is not in map 945 | let c = m.get(&'a').unwrap_or(&-1); // .get(), c == -1 if a is not in map. no crash. 946 | match m.get(&'a') { // deal with map get() lookup using Match + Option 947 | Some(x)=>println!("a found in map, value is {}",x), 948 | None=>println!("a not found in map"), 949 | } 950 | if let Some(x) = m.get(&'a') { // deal with map get() lookup using if let + Option 951 | println!("a found in map, value is {}",x); 952 | } // if 'a' is not in map, do nothing 953 | 954 | *m.get_mut(&'a').unwrap() += 2; // change a value inside a map 955 | 956 | ``` 957 | 958 | Concise initialization with from and from_iter using tuples of (key,value) 959 | 960 | ```rust 961 | let mut phonecodes = HashMap::from( // from uses a fixed size array 962 | [("Afghanistan",93),("American Samoa",1),("Ukraine",380)] ); 963 | let mut squares:HashMap = HashMap::from_iter( 964 | (0..24).map(|i| (i, i*i)) ); // from_iter uses an iterator 965 | println!("{:?}",phonecodes); //{"Afghanistan: 93, Ukraine: 380... 966 | println!("{:?}",squares); //{10: 100, 3: 9, 1: 1, 13: 169, ... 967 | ``` 968 | 969 | HashSet 970 | 971 | ```rust 972 | use std::collections::HashSet; 973 | let mut squares = HashSet::new(); 974 | squares.insert(0); 975 | squares.insert(4); 976 | let b = squares.contains(&4); // b==true 977 | ``` 978 | 979 | ## Macros 980 | 981 | Does not act like a preprocessor. It replaces items in the abstract syntax tree 982 | 983 | ```rust 984 | macro_rules! hello { 985 | ($a:ident) => ($a = 5) 986 | } 987 | let mut a = 0; 988 | println!("{}",a); // 0 989 | hello!(a); 990 | println!("{}",a); // 5 991 | 992 | macro_rules! bellana { 993 | ($a:expr,$b:expr) => ($a + $b) 994 | } 995 | println!("{:?}",bellana!(5,(9*2))); // 23 996 | 997 | macro_rules! maximum { 998 | ($x:expr) => ($x); 999 | ($x:expr, $($y:expr),+) => ( std::cmp::max($x, maximum!($($y),+)) ) 1000 | } 1001 | maximum!(1,2,3,4); // 4 1002 | 1003 | macro_rules! dlog { 1004 | ($loglevel:expr, $($s:expr),*) => ( 1005 | if DLOG_LEVEL>=$loglevel { println!($($s),+); } 1006 | ) 1007 | } 1008 | let DLOG_LEVEL=5; 1009 | dlog!(4,"program is running, dlog:{}",DLOG_LEVEL); // "program is running, dlog:5" 1010 | 1011 | /* 1012 | designators: 1013 | block // rust block, like {}. expr // expressions 1014 | ident // variable/function names. item // 1015 | pat // pattern. path // rust path 1016 | stmt // statement. tt // token tree 1017 | ty // type. vis // visibility qualifier 1018 | */ 1019 | ``` 1020 | 1021 | 1022 | 1023 | 1024 | ## Arrays, Slices, Ranges 1025 | 1026 | Arrays 1027 | 1028 | ```rust 1029 | let arr: [u8; 4] = [1, 2, 3, 4]; // immutable array. cant change size. 1030 | let mut arrm: [u8; 4] = [1,2,3,4]; // mutable array. cant change size. 1031 | let n = arr.len(); // length of array = 4 items 1032 | let s1 = &arr[0..2]; // slice of underlying array 1033 | let n2 = s1.len(); // length of slice = 2 items 1034 | println!("{:?}",s1); // 1 2, contents of slice 1035 | let s2 = &arr[1..]; // slice until end 1036 | println!("{:?}",s2); // 2 3 4 contents of slice until end 1037 | let sm = &mut arrm[0..2]; // mutable slice 1038 | sm[0] = 11; // change element of mutable slice, 1039 | println!("{:?}",sm); // 11 2 3 4 1040 | // underlying array was changed 1041 | println!("{:?}",arrm); // 11 2 3 4 1042 | 1043 | let z = [1,6,1,8,0,0,3]; 1044 | println!("{:?}",z[0..4]); // error - not a slice 1045 | ^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time 1046 | println!("{:?}",&z[0..4]); // OK to take a slice 1047 | // 1,6,1,8 1048 | 1049 | // pass array slice to function 1050 | fn dostuff(x:&mut [u8]) { 1051 | x[0] = 5; 1052 | println!("{:?} {}",x,x.len()); // 5 2 3 4 4 1053 | } 1054 | 1055 | fn main() { 1056 | let mut arr: [u8; 4] = [1, 2, 3, 4]; 1057 | dostuff( &mut arr ); 1058 | } 1059 | 1060 | ``` 1061 | 1062 | Get(), a byte index into a UTF8 String 1063 | 1064 | ```rust 1065 | let sa = String::from("a"); // get() - byte index slice from a String 1066 | let sga = sa.get(0..1); // Some("a"). get returns Option, but not a Option 1067 | let s = String::from("上善若水"); // get() with a UTF8 String where char.len()!=1 1068 | let sg0 = s.get(0..0); // Some("") , the empty string. 1069 | let sg1 = s.get(0..1); // None, because the first byte of 上 is not a utf8 char 1070 | let sg2 = s.get(0..2); // None, same reason 1071 | let sg3 = s.get(0..3); // Some("上"), this works because 上 in utf8 is 3 bytes 1072 | let sg4 = s.get(0..4); // None again, because we now have 上 + 1 other byte 1073 | // see 上 at http://www.unicode.org/cgi-bin/GetUnihanData.pl?codepoint=4E0A 1074 | ``` 1075 | 1076 | ## Split_At_Mut - Mutability and References into a Vector 1077 | 1078 | For the special case for getting references to items within a vector: 1079 | 1080 | Imagine we have three Wheels (struct W) with radius (W.r) and they are inside 1081 | a vector v. We start with wheels of radius 3,4,5 and want to change 1082 | it to be radiuses of 2, 4, 8. 1083 | 1084 | ```rust 1085 | #[derive(Debug)] 1086 | struct W{ r:u32 } // wheel struct 1087 | let mut v = vec![W{r:3},W{r:4},W{r:5}]; // vector of structs 1088 | let wheela = &mut v[0]; // mutable reference to Wheel with radius 3 1089 | let wheelb = &mut v[2]; // compile error! two mutables for one variable! 1090 | // error[E0499]: cannot borrow `v` as mutable more than once at a time 1091 | wheela.r = 2; // nope 1092 | wheelb.r = 8; // nope 1093 | ``` 1094 | The borrow checker treats the entire vector as one gigantic variable, so 1095 | you can't edit part of it with a mutable reference and then edit another part, 1096 | because the parts aren't considered parts. They are considered as if you are 1097 | editing the variable v. 1098 | 1099 | But there is a workaround built into the language. It is called.. 1100 | 1101 | split_at_mut 1102 | 1103 | It can create mutable slices, which allow mutable access to the vector. 1104 | 1105 | ```rust 1106 | #[derive(Debug)] 1107 | struct W{ r:u32 } // wheel struct 1108 | let mut v = vec![W{r:3},W{r:4},W{r:5}]; // vector of structs 1109 | let (l, r) = v.split_at_mut(2); // split after the wheel with r4 1110 | let wheela = &mut l[0]; // first item of left part of split 1111 | let wheelb = &mut r[0]; // first item of right part of split 1112 | wheela.r = 2; // no problem 1113 | wheelb.r = 8; // no problem 1114 | println!("{:?} {:?}",l,r); // [W { r: 2 }, W { r: 4 }] [W { r: 8 }] 1115 | println!("{:?}",v); // [W { r: 2 }, W { r: 4 }, W { r: 8 }] 1116 | ``` 1117 | 1118 | ## Object-Oriented alternatives 1119 | 1120 | Inheritance and Polymorphism - Rust doesn't have the object-oriented style of these things. Alternatives: 1121 | 1122 | - "composition" (struct within struct), 1123 | - Traits (sort of like Interfaces), 1124 | - Enums (which can do much more than C enums, they are more like Tagged Unions aka Algebraic Data Types aka Sum Types) 1125 | - "NewType" pattern, aka Tuple Structs where for example you say struct MyWrapper(u32) to wrap u32 and then impl your own methods on MyWrapper to mimic u32, along with derive_more and implement deref (u32 could be any other struct or type from another external crate). 1126 | - todo - describe Dynamic Trait Objects 1127 | - todo - describe Generics 1128 | 1129 | ## Files 1130 | 1131 | ```rust 1132 | use std::fs::File; // File handling module 1133 | use std::io::{Write,Read} // read and write capabilities for File 1134 | use std::fs::OpenOptions; // specialized version of File 1135 | 1136 | // read file, non-crashing example 1137 | let filename = "test.txt"; 1138 | match File::open(filename) { 1139 | Err(why) => println!("failed to open file '{}': {}", filename, why), 1140 | Ok(mut f) => { 1141 | println!("ok, opened {}",filename); 1142 | let mut data = String::new(); 1143 | match f.read_to_string( &mut data ) { 1144 | Err(why) => println!("failed to read {}, {}",filename,why), 1145 | Ok(n) => println!("read {} bytes",n), 1146 | }; 1147 | }, 1148 | } 1149 | 1150 | // crash-on-error examples ( unwrap() calls panic! on error, crashes the program ) 1151 | let mut s = String::new(); 1152 | File::open("test.txt").unwrap().read_to_string(&mut s).unwrap(); // read into s 1153 | File::create("output.txt").unwrap().write(s.to_bytes()).unwrap(); // write string 1154 | f = File::create("output.txt").unwrap(); // create if doesnt exist 1155 | f = OpenOptions::new().write(true).truncate(true).create(true).open("out.txt").unwrap(); // create if doesnt exist 1156 | f = OpenOptions::new().append(true).open("out.txt").unwrap(); // append 1157 | f = OpenOptions::new().append(true).create(true).open("out.txt").unwrap(); // append + create 1158 | write!(f,"{}",s).unwrap(); // write formatted string, given file object f 1159 | 1160 | let mut v = vec![]; // read binary data, vec of u8 1161 | f = File::open("test.bin").unwrap().read_to_end(&mut v); // without using buffering 1162 | 1163 | use std::io::{self,BufRead}; // read from stdin aka standard input 1164 | let line = io::stdin().lock().lines().next().unwrap().unwrap(); 1165 | 1166 | let x = include!("datafile.txt"); // include external data file, example: vec![0,0,0]; 1167 | 1168 | // handle errors inside a io::Result-returning function with the question mark ? 1169 | fn readfunc() -> std::io::Result<()> { 1170 | let mut tmpbuf = vec![0u8;4096]; 1171 | let mut f = File::open("somefile.bin")?; // instead of unwrap, just return error 1172 | let count = f.read( &mut tmpbuf )?; // instead of match, just return error 1173 | Ok(()) // we got this far, so there's no error 1174 | } 1175 | 1176 | // read binary data in a loop to a buffer 1177 | fn readfunc2() -> std::io::Result<()> { 1178 | let mut tmpbuf = vec![0u8;4096]; 1179 | let mut f = File::open("somefile.bin")?; 1180 | loop { 1181 | let numbytes_read = f.read( &mut tmpbuf )?; 1182 | println!("read data {}",numbytes_read); 1183 | if numbytes_read==0 { break Ok(()); } 1184 | } 1185 | } 1186 | 1187 | // same loop as above, but with While Let 1188 | fn readfunc3() -> std::io::Result<()> { 1189 | let mut tmpbuf = vec![0u8;4096]; 1190 | let mut f = File::open("somefile.bin")?; 1191 | while let numbytes_read = f.read( &mut tmpbuf )? { 1192 | println!("read # bytes: {}",numbytes_read); 1193 | if numbytes_read==0 { break; } 1194 | }; 1195 | Ok(()) 1196 | } 1197 | 1198 | 1199 | // Passing File to function... 1200 | pub fn zipread( mut rd:&File ) { let x = rd.read(&[buf])?; println!("{}",x); } 1201 | // .. or... pass any struct with read trait 1202 | pub fn zipread( mut rd:impl Read ) { let x = rd.read(&[buf])?; println!("{}",x); } 1203 | 1204 | // File position 1205 | // note this here is a u64 so y'all with a exabyte of data gone hafta finda workaroun' 1206 | // also for some reason f has to be mutable if you do this. 1207 | use std::io::Seek; 1208 | println!("{:?}",f.stream_position()); 1209 | 1210 | // there is no 'close', files close automatically at the end of scope ( "}" ) 1211 | 1212 | ``` 1213 | 1214 | ### Filesystem 1215 | 1216 | ```rust 1217 | use std::fs; 1218 | match std::fs::copy( "file.txt", "newfile.txt") { 1219 | Ok => println!("copy successfull"), 1220 | Err(why) => println!("copy failed"), 1221 | } 1222 | std::fs::remove_file("newfile.txt").unwrap(); // panic if failure 1223 | 1224 | ``` 1225 | 1226 | ## System, arguments to main(), environment variables 1227 | 1228 | ```rust 1229 | std::env::args().for_each(|x| print!("{} ",x)); // main arguments as iterator, print each one 1230 | for arg in std::env::args().collect::>() { print!("{} ",arg); }; // same, as vector 1231 | if std::env::args().any(|x| x=="--help") {help()}; // if called with --help, run help() 1232 | let progname = std::env::args().nth(0) // first argument. but this wont compile! its an Option() 1233 | let progname = std::env::args().nth(0).unwrap_or("yr system is very broken".to_string()); 1234 | // on most systems, first argument = program name. but args is not guaranteed to exist, its an iterator 1235 | // that could be empty. so we can use unwrap_or() to deal with the Option if no arguments are there 1236 | ``` 1237 | 1238 | ### clap - command line argument parser 1239 | 1240 | # if you want your program 'mycompiler' to have args like this: 1241 | mycompiler input.txt -o binary.exe -optimize -I./include -I/usr/include 1242 | # install clap crate: 1243 | cargo add clap --features derive # bash command to add clap dependency 1244 | 1245 | ```rust 1246 | mycompiler.rs: 1247 | extern crate clap; 1248 | // clap will parse the struct and automatically handle errors, 1249 | // detect the argument type based on the variable (Vec,Option,bool) 1250 | // with Option for optional args, no Option for Requred args, Vec for 1251 | // multiple args, bool for flag style args, etc. clap will also 1252 | // automatically provide --verison and --help using /// comments 1253 | #[derive(clap::Parser, Debug)] 1254 | pub struct Args { 1255 | /// inputfile - filename to compile 1256 | inputfile: String, 1257 | 1258 | /// outfile - filename for binary output 1259 | #[arg(short = 'o', long)] 1260 | outfile: Option(String), 1261 | 1262 | /// paths to search for header files 1263 | #[arg(short = 'I', long)] 1264 | include: Vec, 1265 | 1266 | /// use the optimizer during compilation 1267 | #[arg(short='O', long)] 1268 | optimize: bool 1269 | } 1270 | 1271 | fn main() { 1272 | let clapargs = Args::parse_from(std::env::args.clone()); 1273 | println!("{:?}",clapargs); 1274 | let mut output = "a.out"; 1275 | if let Some(outname) = clapargs.outfile.as_deref() { output = outname}; 1276 | if let Some(input) = clapargs.inputfile.as_deref() { 1277 | compile(input,output,clapargs.optimize); 1278 | } 1279 | } 1280 | 1281 | ``` 1282 | 1283 | ### Environment variables 1284 | ```rust 1285 | std::env::var("IGNORE_CASE") // environment variable IGNORE_CASE, returns Result or Err 1286 | if let Ok(v) = std::env::var("SHLVL") {println!("{:?}",v);} // print SHLVL if it's there, otherwise ignore 1287 | let sl = match std::env::var("SHLVL") { Ok(v)=>v,Err(e)=>"not set".to_string() }; // set s1 to SHLVL or "not set" if unset 1288 | println!("{:?}",std::env::vars().collect::>()); // print all environment variables 1289 | for v in std::env::vars() {println!("{:?}",v);} // print env vars line by line 1290 | ``` 1291 | 1292 | 1293 | ## Reflection 1294 | 1295 | For enums: 1296 | ``` 1297 | use strum::IntoEnumIterator; 1298 | #[derive(strum_macros::EnumIter)] 1299 | enum Attachment { Avoidant, Anxious, Secure }; 1300 | Attachment::iter().for_each(|d|print!("{d:?},")); // Avoidant, Anxious, Secure 1301 | ``` 1302 | 1303 | For others: todo 1304 | 1305 | ## Traits 1306 | 1307 | todo. Traits are like 'interfaces' in other languages. 1308 | You can see examples above where a Struct will Derive the Debug trait 1309 | so that gives it certain abilities. Traits are used extensively 1310 | when dealing with iterators, see below. 1311 | 1312 | ## Iterators, functional style programming 1313 | 1314 | ```rust 1315 | let v = vec![3,4,5]; 1316 | let it = v.iter(); // iterator as object 1317 | println!("{:?} {:?} {:?}",it.next(),it.next(),it.next()); // consumed 1318 | println!("{:?}",it.next()); // None 1319 | 1320 | let v = vec![3]; 1321 | let mut i = v.iter().peekable(); 1322 | println!("{:?}",i.peek()); // Some(3) 1323 | println!("{:?}",i.peek()); // Some(3) 1324 | println!("{:?}",i.next()); // 3 1325 | println!("{:?}",i.next()); // None 1326 | 1327 | let mut i = v.iter(); 1328 | print!("{:?}",i.collect::>(); // iterator back to vector 1329 | 1330 | // basic iteration 1331 | for i in v.iter() {print!("{i} ");} // 3 4 5 1332 | vec![3,4,5].iter().for_each(|x| print!("{x} ")); // 3 4 5 1333 | let biggest = v.iter().max(); // 5 1334 | let hasle2 = v.iter().any(|x| x<=4); // true if any element is less than or equal to 2 1335 | let biggest = v[0..1].iter().max(); // will return 4, not 5, b/c we took a slice of the vector 1336 | for i in vec![3,4,5].iter().take(2) {print("{i}");} // 3, 4 1337 | for (i,n) in vec![3,4,5].iter().enumerate() {print!("{i}:{n} ");} // 0:3 1:4 2:5 1338 | vec![3,4,5,3].iter().find(|&&x| x == 3) // Some(&3), first three 1339 | vec![3,4,5].iter().position(|&&x| x == 5) // 2 // kind of like indexOf() in other langs 1340 | 1341 | // combine multiple iterators 1342 | (3..=5).chain(9..=11).for_each(|i|print!("{i:?} ")); // 3 4 5 9 10 11 1343 | (3..=5).zip(9..=11).for_each(|i|print!("{i:?} ")); // (3, 9) (4, 10) (5, 11) 1344 | 1345 | // skipping elements 1346 | for i in v.iter().step_by(2) {print!("{i} ");} // 3 5 vec![ 1347 | for i in vec![3,4,5].iter().skip(1) {print("{i}");} // 4, 5 1348 | print!("{:?}",vec![3,4,5].into_iter().map(|x| 2 * x).collect::>()); // 6 8 10 1349 | for i in vec![3,4,5].iter().filter(|x| x<=4) {print!("{i}");} // 3 4 1350 | for i in vec![Some(3),None,Some(4)].iter().fuse() {print!("{i}");} // Some(3), None 1351 | vec![4,5,3,3].iter().skip_while(|x| **x>=4).for_each(|x|print!("{},",x)); // 3,3, 1352 | for i in vec![3,4,5,3].iter().skip_while(|x| **x>=4) {print!("{i},");} // 3,4,5,3 1353 | for i in vec![4,5,3,3,9,6].iter().take_while(|x| **x>=4) {print!("{i},");} // 4,5 1354 | for i in vec![3,4,5,3].iter().take_while(|x| **x>=4) {print!("{i},");} // (nothing) 1355 | vec![3,4,5].iter().cycle().take(6).for_each(|x|print!("{},",x)); // 3,4,5,3,4,5 cycle allows wraparound 1356 | 1357 | // modify elements 1358 | v.iter_mut().for_each(|v|*v=*v+5); 1359 | 1360 | // mathematical operations 1361 | for i in vec![3,4,5].iter().scan(0,|a,&x| {*a=*a+x;Some(*a)}) {print!("{i}")} // 3,7,12 1362 | print!("{}",vec![3,4,5].iter().fold(0, |a, x| a + x)); // 12 1363 | vec![3,4,5].iter().sum() // 3+4+5, 12 1364 | vec![3,4,5].iter().product() // 12*5, 60 1365 | (1..4).reduce(|x,y| x*y).unwrap_or(0); // 6 // 6 is 1*2*3 1366 | println!("{}",vec![1.,2.,3.].iter().cloned().fold(std::f64::MIN, f64::max)); // max, 3 1367 | println!("{}",vec![1.,2.,3.].iter().cloned().fold(std::f64::MAX, f64::min)); // min, 1 1368 | print!("{:?}",vec![['a','b','c'], ['d','e','f']].iter().flatten().collect::() ); // "abcdef" 1369 | print!("{:?}",vec![vec![3,4],vec![5,1]].iter().flatten().collect::>()); // 3,4,5,1 1370 | let (a,b): (Vec<_>, Vec<_>) = vec![3,4,5].into_iter().partition(|x| x>&4); 1371 | println!("{:?} {:?}",a,b); // [5] [3,4] 1372 | vec![3,4,5].iter().all(|x| x>2) // true 1373 | vec![3,4,5].iter().all(|x| x>4) // false 1374 | vec![3,4,5].iter().any(|x| x<4) // true 1375 | vec![3,4,5].iter().any(|x| x<2) // false 1376 | // comparisons: cmp, le, eq, ne, lt, etc 1377 | print!("{}",vec![3,4,5].iter().eq(vec![1,3,4,5].iter().skip(1))); // true 1378 | 1379 | // misc 1380 | permutations(n) // n-based permutations of each element of the iterator 1381 | unique() // remove duplicates from iterators 1382 | cloned() // clones each element 1383 | unzip() // backwards of zip() 1384 | rev() // reverse iterator 1385 | rposition() // combine reverse and position 1386 | max_by_key() // max using single func on each item 1387 | max_by() // max using compare closure |x,y| f(x,y) 1388 | v.min_by(|a,b| a.x.partial_cmp(&b.x).unwrap_or(std::cmp::Ordering::Equal)); //min val,float 1389 | find_map() // combine find and map 1390 | flat_map() // combine flatten and map 1391 | filter_map() // combine filter and map 1392 | 1393 | into_iter() // can help you collect 1394 | let (v,u)=(vec![1,2,3],vec![4,5,6]); 1395 | print!("{:?}", v.into_iter().zip(u.into_iter()).collect>()); // E0277 1396 | // value of type `Vec<(i8, i8)>` cannot be built from `std::iter::Iterator>()); // [(1, 4), (2, 5), (3, 6)] 1398 | ``` 1399 | 1400 | 1401 | 1402 | Itertools library: more adapters 1403 | ```rust 1404 | use itertools::Itertools; 1405 | // these are periodically integrated into Rust core, and/or may change 1406 | 1407 | // sort related 1408 | vec![13,1,12].into_iter().sorted(); // [1,12,13] 1409 | vec![(3,13),(4,1),(5,12)].into_iter().sorted_by(|x,y| Ord::cmp(&x.1,&y.1)); // [(4,1),(5,12),(3,13)] 1410 | "Mississippi".chars().dedup().collect::(); // Misisipi 1411 | "agcatcagcta".chars().unique().collect::(); // agct 1412 | 1413 | // mingling single items into others 1414 | "四四".chars().join("十")); // 四十四 1415 | (1..4).interleave(6..9).collect_vec(); // 1,6,2,7,3,8 1416 | " 十是十 ".chars().intersperse('四').collect::(); // "四十四是四十四" 1417 | "愛".chars().pad_using(4,|_| '.').collect::(); // "愛..." 1418 | 1419 | // multiple iterators 1420 | "az".chars().merge("lm".chars()).collect::(); // "almz" 1421 | "za".chars().merge_by("ml".chars(),|x,y| x>y ).collect::(); // "zmla" 1422 | vec!["az".chars(),"lm".chars()].into_iter().kmerge().collect::(); //"almz" 1423 | for c in &vec![1,2,3,4,5].into_iter().chunks(2) { // chunk - split into new iterator groups of 2 1424 | for x in c { print!("{:?}",x); } print!(","); } // each group is 2 items. output: 12,34,5, 1425 | 1426 | // mathematical operations 1427 | "ab".chars().cartesian_product("cd".chars()).collect_vec() ; // [(a,b),(a,d),(b,c),(b,d)] 1428 | (1..=3).combinations(2).collect_vec(); // [[1,2], [1,3], [2,3]] 1429 | "Go raibh maith agat".chars().positions(|c| c=='a').collect_vec();// [4, 10, 15, 17] 1430 | ("四十四是四十四".chars().all_equal(), "謝謝".chars().all_equal()); // (false, true) 1431 | [1,5,10].iter().minmax().into_option().unwrap() // (1,10) // faster than min() then max() 1432 | (0..3).zip_eq("abc".chars()).collect_vec(); // [(0,'a'), (1,'b'), (2,'c')] 1433 | 1434 | // modifying and removing items 1435 | "bananas".chars().coalesce(|x,y| if x=='a'{Ok('z')}else{Err((x,y))}).collect::(); 1436 | // result = bzzz - coalesce will replace a pair of items with a new single item, if the item matches 1437 | vec![1,2,3].into_iter().update(|n| *n *= *n).collect_vec(); // [1,4,9] 1438 | let mut s=['.';3];s.iter_mut().set_from("abcdefgh".chars()); s.iter().collect::(); // "abc" 1439 | ``` 1440 | 1441 | ## Implementing your own iterator for your own struct 1442 | 1443 | If you want to be able to use all the cool adapters like filter(), map(), fold(), etc, 1444 | on your own cusom data structure you can implement the Iterator trait for your 1445 | struct. Basically you need to create another struct called a "XIterator" that implements 1446 | a 'next()' method. Then you create a method in your own struct called 'iter()' that returns 1447 | a brand new XIterator struct. 1448 | 1449 | Here is a super simple example, all it does is iterate over a data inside of custom 1450 | data struct Mine. The data is stored in a vector so it's pretty straightforward to access it. 1451 | 1452 | 1453 | ```rust 1454 | 1455 | #[derive(Debug)] 1456 | struct Mine{ 1457 | v:Vec 1458 | } 1459 | 1460 | impl Mine{ 1461 | fn iter(self:&Mine) -> MineIterator { 1462 | MineIterator { 1463 | m:&self, count:0, 1464 | } 1465 | } 1466 | } 1467 | 1468 | struct MineIterator<'a> { 1469 | m: &'a Mine, 1470 | count: usize, 1471 | } 1472 | 1473 | impl<'a> Iterator for MineIterator<'a> { 1474 | type Item = &'a u8; 1475 | fn next(&mut self) -> Option { 1476 | self.count+=1; 1477 | if self.count<=self.m.v.len() { 1478 | Some(&self.m.v[self.count-1]) 1479 | } else { 1480 | None 1481 | } 1482 | } 1483 | } 1484 | 1485 | fn main() { 1486 | let m=Mine{v:vec![3,4,5]}; 1487 | m.iter().for_each(|i| print!("{:?} ",i));println!(""); // 3 4 5 1488 | m.iter().filter(|x|x>3).fold(0,|a,x| a+x); // 9 1489 | } 1490 | 1491 | ``` 1492 | 1493 | If you want to create a Mutable Iterator, you will (as of writing, 2018) 1494 | have to use an unsafe code with a raw pointer. This is alot like what the 1495 | standard library does. If you are not extremely careful, your program may 1496 | exhibit bizarre behavior and have security bugs, which defeats the purpose 1497 | of using Rust in the first place. 1498 | 1499 | * https://gitlab.com/Boiethios/blog/snippets/1742789 1500 | * https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer 1501 | 1502 | ```rust 1503 | 1504 | 1505 | impl Mine{ 1506 | fn iter_mut(self:&mut Mine) -> MineIterMut { 1507 | MineIterMut { 1508 | m:self, count:0,//_marker:std::marker::PhantomData, 1509 | } 1510 | } 1511 | } 1512 | 1513 | pub struct MineIterMut<'a> { 1514 | m: &'a mut Mine, 1515 | count: usize, 1516 | } 1517 | 1518 | impl<'a> Iterator for MineIterMut<'a> { 1519 | type Item = &'a mut u8; 1520 | fn next(&mut self) -> Option<&'a mut u8> { 1521 | if self.count == self.m.v.len() { 1522 | None 1523 | } else { 1524 | let ptr: *mut u8 = &mut self.m.v[self.count]; 1525 | self.count += 1; 1526 | unsafe{ Some(&mut *ptr) } 1527 | } 1528 | } 1529 | } 1530 | 1531 | fn main() { 1532 | let mut m=Mine{v:vec![3,4,5]}; 1533 | m.iter().for_each(|i| print!("{:?} ",i));println!(""); // 3 4 5 1534 | m.iter_mut().for_each(|i| *i += 1); 1535 | m.iter().for_each(|i| print!("{:?} ",i));println!(""); // 4 5 6 1536 | } 1537 | ``` 1538 | 1539 | ### make your own Iterator with impl Iterator 1540 | 1541 | "impl Iterator" can help build iterators without creating your own custom struct. 1542 | For example you can return an Iterator from a function that modifies your data. Examples 1543 | follows for Itertools iproduct! macro and cartesian_product adapter. 1544 | 1545 | ```rust 1546 | fn f2() -> impl Iterator { iproduct!(0..2,0..3) } 1547 | fn f3(mut v:Vec) -> impl Iterator { 1548 | v.push(1); v.into_iter().cartesian_product(0..3) } 1549 | 1550 | for vertex in f2() {println!("{:?}", vertex);} // (0,0) (0,1) (0,2) (1,0) (1,1) (1,2) 1551 | for vertex in f3(vec![0]) {println!("{:?}", vertex);} // (0,0) (0,1) (0,2) (1,0) (1,1) (1,2) 1552 | ``` 1553 | 1554 | ## Math 1555 | 1556 | ### arithmetic 1557 | 1558 | ```rust 1559 | 1560 | let x=250u8; // addition can crash, max value of u8 is 255 1561 | println!("{}",x+10); // crashes, attempt to add with overflow 1562 | println!("{}",x.wrapping_add(10)); // wraps around, result: 5 1563 | println!("{}",x.saturating_add(10)); // stays at max, result: 255 1564 | println!("{}",x.wrapping_sub(u8::MAX)); // wraparound subtraction 1565 | std::f32::MIN; // minimum binary floating point 32 bit value 1566 | std::i32::MAX; // maximum 32 bit integer value 1567 | 1568 | let b = -5; 1569 | let c = b.abs(); // error, abs not defined on generic integer 1570 | let b = -5i32; 1571 | let c = b.abs(); // ok, abs is defined on i32, 32 bit integer 1572 | 42.25f32.round(); // can also call functions on float literals 1573 | 9.0f32.sqrt(); // square root approximation 1574 | 9.sqrt() // error, sqrt only for floats 1575 | let x = 3/4; // 0 1576 | let y = 3.0/4; // error, no implementation for `{float} / {integer}` 1577 | let z = 3.0/4.0; // 0.75 1578 | ``` 1579 | 1580 | Exponentials, exponentiation, power, raising to a power 1581 | 1582 | ```rust 1583 | let p1 = 2.pow(32) //err // error[E0689]: can't call method `pow` on ambiguous numeric type `{integer}` 1584 | let p2 = 2_u8.pow(32) //err // thread 'main' panicked at 'attempt to multiply with overflow' 1585 | let p3 = 2_u8.checked_pow(32); // v becomes "None". 1586 | let p4 = match 2_u8.checked_pow(32) { Ok(n)=>n,None=>0 } // match a checked_pow b/c it returns Option 1587 | let p5 = 2u8.checked_pow(k).unwrap_or(0); // 0 if overflow, simpler than match 1588 | let p6 = 2_u64.pow(32) // ok 1589 | ``` 1590 | 1591 | ### data conversion 1592 | 1593 | ```rust 1594 | let x: u16 = 42; // integer conversion. start with u16 1595 | let y: u8 = x as u8; // cast to unsigned 8 bit integer 1596 | let z: i32 = x as i32; // cast to signed 32 bit integer 1597 | let bez = z.to_le_bytes(); // get individual 8 bit bytes in the 32 bit int 1598 | let bbz = z.to_be_bytes(); // same, but in big endian order 1599 | println!("{:?},{:?},{:?},{:?}",bez[0],bez[1],bez[2],bez[3]); // 42,0,0,0 1600 | println!("{:?},{:?},{:?},{:?}",bbz[0],bbz[1],bbz[2],bbz[3]); // 0,0,0,42 1601 | 1602 | let a = u32::from('A') // get integer value of char. all chars are 4 bytes. 1603 | let a = char::from_u32(68) // get char value of an integer (utf8/ascii) 1604 | let a = char::from_digit(4,10) // get char '4' from integer 4, base 10 1605 | 1606 | let s = [0,1,2,3]; // u8 to u32, start with array of four u8 1607 | let ls = u32::from_le_bytes(s); // convert to u32, little endian 1608 | let bs = u32::from_be_bytes(s); // convert. to u32, big endian 1609 | println!("{:?} {:?}",ls,bs); // 50462976 66051 1610 | let s2 = vec![0,0,0,1,2,3,4,5,6,7]; // vector of ten u8 1611 | let ars2 = [s2[2],s2[3],s2[4],s2[5]]; // array of four u8 1612 | let be = u32::from_be_bytes(ars2); // create u32 from 3rd-6th bytes of ars2 1613 | println!("{:?}",be); // 66051 1614 | 1615 | // converting vectors of integers 1616 | let mut v1 = vec![0u8;2]; // 2 u8, each with '0' 1617 | let v2 = vec![0xffeeaa00u32,0xff0000dd]; // two u32 in a vector 1618 | v2.extend(v2); // error the trait bound `Vec: Extend` is not satisfied 1619 | for d in v2 { v1.extend(d.to_le_bytes()) }; // convert each u32 by itself 1620 | println!("{:?}",v1) // [0, 0, 0, 170, 238, 255, 221, 0, 0, 255] 1621 | 1622 | // integer conversion using byteorder crate 1623 | extern crate byteorder; // modify your Cargo.toml to add byteorder crate. then: 1624 | use byteorder::{BigEndian, ReadBytesExt, NativeEndian, ByteOrder, LittleEndian, WriteBytesExt}; 1625 | let arr = [0,1,2,3]; 1626 | let s = NativeEndian::read_u32(&arr[0..4]); // array of four bytes into u32. 1627 | let mut v = vec![0u8]; 1628 | s.write_u8( v ); 1629 | 1630 | let vx = vec![0u8,1,2,0xff,4,5,6,0xff]; // vector of u8 1631 | //let mut vy = vec![0u32]; // vector of u32 1632 | //LittleEndian::read_u32_into(&vx,&mut vy); // panic, vy cant hold vx 1633 | let mut vy = vec![0u32;2]; // vector of u32 1634 | LittleEndian::read_u32_into(&vx,&mut vy); // convert u8s to u32s 1635 | println!("{:x?}",vy); // [ff020100, ff060504] 1636 | let mut vx2 = vec![0u8;8]; // new vector of u8 1637 | LittleEndian::write_u32_into(&vy,&mut vx2); // convert back to u8s 1638 | println!("{:02x?}",vx); // [00, 01, 02, ff, 04, 05, 06, ff] 1639 | 1640 | // converting vectors of integers using unsafe mem::transmute 1641 | let v = vec![0u8;80]; let i = 0; 1642 | let n:i32 = {unsafe { std::mem::transmute::<&[u8],&[i32]>(&v[i..i+4])}}[0]; 1643 | let x:f32 = {unsafe { std::mem::transmute::<&[u8],&[f32]>(&v[i..i+4])}}[0]; 1644 | let mut block = vec![0u8;64]; // convert entire block at once 1645 | let mut X = unsafe { mem::transmute::<&mut [u8], &mut [u32]>(&mut block) }; 1646 | #[cfg(target_endian = "big")] // deal with endian issues if needed 1647 | for j in 0..16 { X[j] = X[j].swap_bytes(); } 1648 | 1649 | 1650 | 1651 | 1652 | let s = format!("{:e}",0.0f32); // convert float32 to base-10 decimal string (scientific format) 1653 | let n = s.parse::().unwrap(); // parse float from string, panic/crash if theres an error 1654 | let mut n = 0.0f32; // parse float from string, without panic/crashing 1655 | match s.parse::() { 1656 | Err(e)=>println!("bad parse of {}, because {}",s,e), 1657 | Ok(x)=>n=x 1658 | } 1659 | let a = "537629.886026485" // base-10 decimal string to binary float point 1660 | print!("{:.50}",a.to_string().parse::().unwrap();// 537629.875000000000000000000000000000000 1661 | let b = 537629.886026485; print!("{:.50}",b); // 537629.886026485008187592029571533203125 1662 | let c = 537629.886026485f32; print!("{:.50}",c); // 537629.875000000000000000000000000000000 1663 | let d = 537629.886026485f64; print!("{:.50}",d); // 537629.886026485008187592029571533203125 1664 | 1665 | let ch='e'; 1666 | ch.is_digit(16) // true, 'e' is a numerical digit in base 16 1667 | ch.is_digit(10) // false, 'e' is not a numerical digit in base 10, only 0-9 are. 1668 | let y:u32 = ch.to_digit(16).unwrap_or(0); // convert 'e' into 15, since base=16. if fail, make y 0 1669 | let n = 'x'.is_alphabetic(); // detect whether letter 1670 | println!("𝄠 is hex 0x{:06x}, or decimal {}",'𝄠' as u32,'𝄠' as u32); // unicode codepoint 1671 | println!("a is hex 0x{:06x}, or decimal {}",'a' as u32,'a' as u32); // for <128 this is the ascii code 1672 | 1673 | let m = 0b0001; // binary literal 1674 | let m = 0o0007; // octal literal 1675 | let m = 0x000f; // hexadecimal literal 1676 | let (a,b,c) = (0b00_01,0o00_07,0x00_0f); // literals with underscores for ease of reading 1677 | 1678 | println!("{:x}",0x12345678u32.swap_bytes()); // 0x78563412 32-bit byteswap 1679 | 1680 | 1681 | 1682 | ``` 1683 | 1684 | #### string conversion 1685 | 1686 | ```rust 1687 | 1688 | use std::i64; 1689 | let z = i64::from_str_radix("0x1f".trim_start_matches("0x"), 16).unwrap(); // hex string to integer 1690 | let q = i64::from_str_radix("0b10001".trim_start_matches("0b"), 2).unwrap(); // binary string to integer 1691 | 1692 | println!("{:?}","abc".as_bytes()); // &[u8] slice, [97, 98, 99] ( utf8 string into bytes ) 1693 | println!("{:?}","abc".as_bytes().to_vec()); // Vec [97, 98, 99] string into slice into Vector of bytes 1694 | println!("{:?}","ᏣᎳᎩ".as_bytes()); // [225, 143, 163, 225, 142, 179, 225, 142, 169] ( Cherokee utf8 ) 1695 | let s = b"\x61\x62\x63"; // b precedes sequence of bytes, hexadecimal, pseudo-string form 1696 | println!("{}",std::str::from_utf8( s ).unwrap()); // abc // decodes utf8 to string, crash if invalid utf8 1697 | 1698 | println!("{:?}", "सूर्य नमस्कार्कार".as_bytes()); // [224, 164, 184,...] 1699 | let s = [224, 164, 184, 224, 165, 130, 224, 164, 176, 224, 165, 1700 | 141, 224, 164, 175, 32, 224, 164, 168, 224, 164, 174, 224, 1701 | 164, 184, 224, 165, 141, 224, 164, 149, 224, 164, 190, 224, 164, 176]; 1702 | println!("{}",std::str::from_utf8( &s ).unwrap()); // सूर्य नमस्कार 1703 | println!("{:?}",std::str::from_utf8( &s ).unwrap()); // "स\u{942}र\u{94d}य नमस\u{94d}कार" different decoding 1704 | ``` 1705 | 1706 | ```rust 1707 | let s = "hello" ; // s = &str 1708 | let s = "hello".to_string(); // s = String 1709 | let m = s.replace("hello","new"); // m = "new" 1710 | let y = s.to_uppercase(); // y = "NEW" 1711 | 1712 | // str.as_bytes() converts to slice, which implements Read 1713 | let s = "Reedeth Senek, and redeth eek Boece"; 1714 | let s2= "Ther shul ye seen expres that it no drede is" 1715 | let buf = &mut vec![0u8; 64]; 1716 | s.as_bytes().read(buf).unwrap(); 1717 | s2.as_bytes().read(&buf[30]).unwrap(); 1718 | ``` 1719 | 1720 | Regular expressions typically use an external crate. 1721 | Syntax for regex: https://docs.rs/regex/1.1.2/regex/index.html#syntax 1722 | 1723 | In cargo.toml, 1724 | [dependencies] 1725 | regex = "1.1.0" 1726 | 1727 | In main.rs: 1728 | ```rust 1729 | use regex::Regex; 1730 | let text = r###"The country is named France, I think the biggest city is Paris; 1731 | the Boulangerie are beautiful on la rue du Cherche-Midi"###; // multiline string 1732 | 1733 | let re = Regex::new(r"country is named (?P.*?),.*?biggest city is (?P.*?);").unwrap(); 1734 | let caps = re.captures(text).unwrap(); 1735 | println!("{}, {}",&caps["biggestcity"],&caps["country"]); // Paris, France 1736 | 1737 | let re = Regex::new(r"(?ms)city is (?P.*?).$.*?beautiful on (?P.*?)$").unwrap(); 1738 | let caps = re.captures(text).unwrap(); 1739 | println!("{}, {}",&caps["streetname"],&caps["citname"]); // la Rue de Cherche Midi, Paris 1740 | ``` 1741 | 1742 | 1743 | ### comparison and sorting 1744 | 1745 | ```rust 1746 | 1747 | use std::cmp // max/min of values 1748 | let a = cmp::max(5,3); // maximum value of 2 integers 1749 | let b = cmp::max(5.0,3.0); // build error, floats cant compare b/c of NaN,Inf 1750 | let v = vec![1,2,3,4,5]; // list, to find max of 1751 | let m = v.iter.max().unwrap(); // max of numbers in a list, crash on error 1752 | match v.iter().max() { // max of numbers in a list, don't crash 1753 | Some(n)=>println!("max {}",n), // do stuff with max value n 1754 | None=>println!("vector was empty") 1755 | } 1756 | let m = v.iter.max().unwrap_or(0); // max of numbers in a list, or 0 if list empty 1757 | 1758 | v = vec![1,3,2]; 1759 | v.sort(); // sort integers 1760 | v = vec![1.0,3.0,2.0]; // sort floats 1761 | v.sort(); // error, float's NaN can't be compared 1762 | v.sort_by(|a, b| b.cmp(a)); // sort integers using closure 1763 | v.sort_by(|a, b| a.partial_cmp(b).unwrap()); // sort floating point using closure 1764 | v.min_by(|a,b| a.x.partial_cmp(&b.x).unwrap_or(std::cmp::Ordering::Equal)); // minimum value 1765 | println!("{}",vec![1.,2.,3.].iter().cloned().fold(std::f64::MIN, f64::max)); // 3 1766 | println!("{}",vec![1.,2.,3.].iter().cloned().fold(std::f64::MAX, f64::min)); // 1 1767 | 1768 | struct Wheel{ r:i8, s:i8}; // sort a vector that holds a struct 1769 | let mut v = vec![Wheel{r:1,s:2},Wheel{r:3,s:2},Wheel{r:-2,s:2}]; 1770 | v.sort_by(|a, b| a.r.cmp(&b.r)); // sort using closure, based on value of field 'r' 1771 | v.sort_by(|a, b| a.r.cmp(&(&b.r.abs()))); // sort using closure, based on abs value of r 1772 | 1773 | fn compare_s( a:&Wheel, b:&Wheel ) -> std::cmp::Ordering { // sort using a function 1774 | a.s.partial_cmp(&b.s).unwrap_or(std::cmp::Ordering::Equal) 1775 | } 1776 | v.sort_by( compare_s ); 1777 | ``` 1778 | 1779 | ### Pseudo randoms 1780 | 1781 | Pseudo Random number generators, aka PRNGs 1782 | 1783 | ```rust 1784 | extern crate rand; /// add rand to dependencies in Cargo.toml 1785 | use rand::prelude::*; 1786 | let x = rand::random(); // boolean 1787 | let y = rand::random::(); // integer 1788 | let x = rand::thread_rng().gen_range(0, 100); // between 1789 | let mut rng = rand::thread_rng(); 1790 | let z: f64 = rng.gen(); // float 0...1 1791 | let mut nums: Vec = (1..100).collect(); 1792 | nums.shuffle(&mut rng); 1793 | ``` 1794 | ```rust 1795 | // alternative, without needing external crate, based on codeproject.com by Dr John D Cook 1796 | // https://www.codeproject.com/Articles/25172/Simple-Random-Number-Generation 1797 | // License: BSD, see https://opensource.org/licenses/bsd-license.php 1798 | use std::time::{SystemTime, UNIX_EPOCH}; 1799 | let mut m_z = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time reversed").as_millis(); 1800 | let mut m_w = m_z.wrapping_add( 1 ); 1801 | let mut prng = || { m_z = 36969 * (m_z & 65535) + (m_z >> 16); 1802 | m_w = 18000 * (m_w & 65535) + (m_w >> 16); 1803 | m_z.rotate_left( 16 ) + m_w }; 1804 | let buf = (0..1000).map(|_| prng() as u8).collect::>(); 1805 | // creates 1000 pseudo random bytes in buf, using Closure named prng 1806 | ``` 1807 | 1808 | ### Hashing 1809 | 1810 | ```rust 1811 | use std::collections::hash_map::DefaultHasher; 1812 | use std::hash::{Hash, Hasher}; 1813 | let mut hasher = DefaultHasher::new(); // hashing, via a Hasher object, which holds state 1814 | let x = 1729u32; 1815 | let y = 137u32; 1816 | hasher.write_u32( x ); // input x to hash func, store output in hasher 1817 | hasher.write_u32( y ); // input y, combine w existing state, store in hasher 1818 | println!("{:x}", hasher.finish()); // .finish() function Hasher gives current state 1819 | 345.hash(&mut hasher); // update hasher's state using '.hash()' trait 1820 | println!("{:x}", hasher.finish()); // .finish() does not 'reset' hasher objects 1821 | 1822 | ``` 1823 | 1824 | ### Date and Time 1825 | 1826 | ```rust 1827 | use std::time::{Instant}; 1828 | let t = Instant::now(); 1829 | // do something for 5.3 seonds 1830 | println!("{}",t.elapsed().as_secs()); // 5, seconds, rounded 1831 | println!("{}",t.elapsed().subsec_millis()); // 300. remainder in milliseconds 1832 | 1833 | use chrono::prelude::*; 1834 | println!("{:?} ", Utc::now().to_rfc2822()); 1835 | println!("{:?} ", Utc::now().format("%Y-%m-%d %H:%M:%S").to_string()); 1836 | println!("{:?} ", Local::now().to_rfc2822()); 1837 | println!("{:?} ",DateTime::parse_from_str("2014-11-28 21:00:09 +09:00", "%Y-%m-%d %H:%M:%S %z")); 1838 | println!("{:?} ",Utc.with_ymd_and_hms(2014, 11, 28, 12, 0, 9).unwrap()); 1839 | println!("{:?} ",Utc.timestamp(1500000000, 0)); // Epoch time seconds, aka mtime on Unix files. 1840 | "Tue, 17 Jan 2023 03:12:07 +0000" 1841 | "2023-01-17 03:12:07" 1842 | "Mon, 16 Jan 2023 21:12:07 -0600" 1843 | Ok(2014-11-28T21:00:09+09:00) 1844 | 2014-11-28T12:00:09Z 1845 | 2017-07-14T02:40:00Z 1846 | ``` 1847 | 1848 | ## Annotations 1849 | 1850 | Aka hashtags aka warning thingies. These statements typically affect compilation and begin with a hashtag, and square brackets. They are sort of like a mix of #pragma and #ifdef from C. 1851 | 1852 | ```rust 1853 | #![allow(dead_code)] // stop compiler from printing warnings on unused funcs/vars, 1854 | #![allow(unused_variables)] // this is good when you are working on a library 1855 | // = note: #[warn(unused_assignments)] on by default 1856 | #![allow(unused_assignments)] // you can take most warnings, and disable by changing 'warn' to 'allow' 1857 | 1858 | // by removing ! and placing on line before a fn, you can disbale warning only for the function 1859 | #[allow(unused_assignments)] 1860 | fn zoogle_poof( n:u8 )->u8 { let a = 0; 5+n }; 1861 | 1862 | let mut a = 0x00ffee22; // modify numbers for big endian machines. 1863 | #[cfg(target_endian = "big")] // target = machine the code will be run on 1864 | a = a.swap_bytes(); // the line (or block) immediately following the # gets affected. 1865 | ``` 1866 | 1867 | ## Linked Lists 1868 | 1869 | Textbook C/Java-style implementations of linked lists often involve ownership that is not allowed by the Rust borrow checker. However it can be accomplished. There is builting "LinkedList" type in std::collections, also some resources from A. Beinges : 1870 | 1871 | https://cglab.ca/~abeinges/blah/too-many-lists/book/ 1872 | 1873 | You can also implement a linked list as a Vector of structs, using integer indexes into the vector instead of pointers. 1874 | 1875 | ## FFI, Calling C functions, porting C code 1876 | 1877 | foreign function interface, aka calling code from other languages. 1878 | 1879 | layout 1880 | ```bash 1881 | src/lib.rs 1882 | src/ccode.c 1883 | build.rs 1884 | Cargo.toml 1885 | ``` 1886 | 1887 | Cargo.toml 1888 | ```rust 1889 | [package] 1890 | name = "duh" 1891 | version = "0.1.0" 1892 | authors = ["akhmatova"] 1893 | build = "build.rs" 1894 | [build-dependencies] 1895 | cc = "1.0" 1896 | libc = "0.2.0" 1897 | ``` 1898 | 1899 | build.rs 1900 | ```rust 1901 | extern crate cc; 1902 | 1903 | fn main() { 1904 | cc::Build::new().file("src/ccode.c").compile("ccode"); 1905 | } 1906 | ``` 1907 | 1908 | src/ccode.c 1909 | ```C 1910 | #include 1911 | int quadrance( int x1, int y1, int x2, int y2) { 1912 | return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2); 1913 | } 1914 | char * blerg( *char txt ) { 1915 | printf("This is C. Here is text from Rust: %s", txt); 1916 | int fd = open("/tmp/blerg",0); 1917 | return "blerg"; 1918 | } 1919 | void printerrs() { 1920 | int code = errno; 1921 | printf("C: errno: %i strerror: %s\n",code,strerror(code)); 1922 | } 1923 | ``` 1924 | 1925 | src/main.rs 1926 | ```rust 1927 | use std::os::raw::{c_int,c_char,c_void}; 1928 | use std::ffi::{CString,CStr}; 1929 | extern "C" { 1930 | fn quadrance(x1: c_int, y1: c_int, x2: c_int, y2: c_int) -> c_int; 1931 | fn blerg(v: *const char)->*const c_char; 1932 | fn printerrs()->c_void; 1933 | } 1934 | fn main() { 1935 | unsafe { 1936 | println!("4^2+3^2={:?}", quadrance(0, 0, 4, 3)); 1937 | let tmp = CString::new("blarg").unwrap(); // CString must be assigned 1938 | let msg = blerg(tmp.as_ptr()); // so as_ptr() will have something to point at 1939 | println!("This is Rust. Here's text from C: {:?}",CStr::from_ptr(msg)); 1940 | printerrs(); // was there an error? 1941 | } 1942 | } 1943 | ``` 1944 | 1945 | Run: 1946 | ```bash 1947 | don@oysters:~/duh$ cargo run 1948 | Compiling duh v0.1.0 (/home/don/duh) 1949 | Finished dev [unoptimized + debuginfo] target(s) in 1.95s 1950 | Running `target/debug/duh` 1951 | 4^2+3^2=25 1952 | This is C. Here is text from Rust: blarg 1953 | This is Rust. Here's text from C: blerg 1954 | ``` 1955 | 1956 | See also: https://s3.amazonaws.com/temp.michaelfbryan.com/index.html 1957 | 1958 | c++ - https://hsivonen.fi/modern-cpp-in-rust/ 1959 | 1960 | https://doc.rust-lang.org/nomicon/ffi.html 1961 | 1962 | Michael Bryan's unofficial guide 1963 | https://s3.amazonaws.com/temp.michaelfbryan.com/index.html 1964 | 1965 | Using char** C functions: 1966 | https://stackoverflow.com/questions/42717473/passing-vecstring-from-rust-to-char-in-c 1967 | 1968 | Note that much C code does not initialize structs or memory, which may be forgotten 1969 | when one is used to Rust 1970 | http://www.ex-parrot.com/~chris/random/initialise.html 1971 | 1972 | Structs and bindgen auto header conversion 1973 | https://medium.com/dwelo-r-d/using-c-libraries-in-rust-13961948c72a 1974 | 1975 | 1976 | Setting CFLAGS examples: 1977 | 1978 | modify build.rs: 1979 | 1980 | ```rust 1981 | extern crate cc; 1982 | 1983 | fn main() { 1984 | std::env::set_var("CFLAGS","-w"); // turn off all warnings 1985 | std::env::set_var("CFLAGS","-v"); // run compiler in verbose mode 1986 | std::env::set_var("CFLAGS","-v -O2"); // optimize level 2 + verbose mode 1987 | cc::Build::new().file("src/ccode.c").compile("ccode"); 1988 | } 1989 | ``` 1990 | 1991 | Unions: 1992 | 1993 | In Rust, Enums are typically preferred over union, as a traditional C-style union is 1994 | only available by using unsafe{}. But unions can help talking to / porting C code: 1995 | ``` rust 1996 | #[repr(C)] 1997 | union Borgle { dorgle: u32, porgle: u8[4] } 1998 | let mut a=Borgle{dorgle:1234}; 1999 | unsafe{ a.dorgle = 0xa0ca0c }; 2000 | ``` 2001 | 2002 | Goto vs labels, loops, and breaks: 2003 | 2004 | Rust does not have Goto, however sometimes a similar result 2005 | can be achieved using labelled loops and breaks. 2006 | 2007 | ```rust 2008 | 'label1: loop { 2009 | if zimblatz == 5 { 2010 | break; // breaks labe1 loop 2011 | } 2012 | 'label2: for frobnoz in 0..4 { 2013 | if blorg==KUMQUAT {break;} // breaks label2 only 2014 | while j<5 { 2015 | if snog==7 { 2016 | // goto end of label1 loop immediately 2017 | break 'label1; 2018 | } 2019 | j+=1; 2020 | } 2021 | } 2022 | } // end of 'label1 loop 2023 | do_something(); 2024 | ``` 2025 | 2026 | Pointer arithmetic 2027 | 2028 | ```rust 2029 | use std::raw::c_uchar; 2030 | unsafe{ 2031 | let mut x = some_c_func() as *const c_uchar; 2032 | x = x.offset(1); // instead of x++ 2033 | } 2034 | ``` 2035 | 2036 | ## source code layout and modules 2037 | 2038 | Here is an example source code organization layout for a simple library, which has a handful of modules, some examples, integration tests, benchmarking, and two executable binaries. 2039 | 2040 | ```bash 2041 | $ ls .. 2042 | ./mycrate # main crate folder 2043 | ./mycrate/Cargo.toml # cargo file, lists dependencies etc 2044 | ./mycrate/src/lib.rs # only one library is allowed per crate 2045 | ./mycrate/src/mod1.rs # module 1. a library can use multiple modules 2046 | ./mycrate/src/mod2.rs # module 2 , a second module 2047 | ./mycrate/src/bin/program1.rs # source code for an executable 2048 | ./mycrate/src/bin/program2.rs # source code for another executable 2049 | ./mycrate/tests # integration tests (as opposed to unit tests at end of every .rs file) 2050 | ./mycrate/tests/integration_test.rs # big test that tests big part of library 2051 | ./mycrate/tests/test_data_file # integration tests often use test files 2052 | ./mycrate/examples # easy to follow examples 2053 | ./mycrate/examples/helloworld.rs # simple example, "use mycrate::*" 2054 | ./mycrate/examples/zoogbnoz.rs # more complicated example 2055 | ./mycrate/benches # benchmarking code and files 2056 | ./mycrate/benches/benchtest.rs # program to run benchmarking speed tests 2057 | ./mycrate/build.rs # optional program to run before build 2058 | ./mycrate/target # binaries are generated under target 2059 | ./mycrate/target/debug/program1 # debug build executable 2060 | ./mycrate/target/release/program1 # release build executable (optimized for speed) 2061 | $ cargo build # this builds all files in crate 2062 | ``` 2063 | 2064 | 2065 | src/lib.rs: (our main library file which the world will use) 2066 | ```rust 2067 | mod mod1; // we must add each modules in lib.rs with 'mod' before we can use them within each other 2068 | use mod1::*; // import symbols from mod1.rs 2069 | pub mod mod2; // this module is public, anyone using this crate can access its functions too 2070 | use mod2::*; // import symbols from mod2.rs 2071 | pub fn mycrate_init() { 2072 | let x = do_stuff1(9); // call function from mod1.rs 2073 | mycrate_monster(x); 2074 | } 2075 | pub fn mycrate_monster(y:u8) -> Monster { // Monster - type from mod1.rs 2076 | let mut a = Monster{ true,y }; 2077 | do_stuff2( &mut a ); // call function from mod2.rs 2078 | a 2079 | } 2080 | pub fn mycrate_calcteeth()-> { 23 } 2081 | ``` 2082 | 2083 | src/mod1.rs: (our 1st module that does stuff) 2084 | ```rust 2085 | pub fn do_stuff1(x:u8)->u8 { x+5 } // public function, available to other modules 2086 | pub struct Monster1 { // public struct, available to other modules 2087 | pub is_happy:bool, // public struct member, available to others 2088 | pub num_teeth:i8, 2089 | } 2090 | ``` 2091 | 2092 | src/mod2.rs: ( 2093 | ```rust 2094 | use super::*; // use functions from lib.rs 2095 | use mod1::*; // use functions/types from mod1.rs, like Monster struct 2096 | // note: this only works if "mod mod1" line is in lib.rs 2097 | // otherwise you get E0432 unresolved import , maybe a missing crate 2098 | pub fn do_stuff2( a: &mut Monster ) { 2099 | a.is_happy = calc_happy(); // call our own private function 2100 | a.num_teeth = mycrate_calcteeth(); // call a function from lib.rs 2101 | } 2102 | fn calc_happy() -> bool { // private function only available within mod2.rs 2103 | match today { Tuesday => true ,_=>false } } 2104 | ``` 2105 | 2106 | See also 2107 | https://doc.rust-lang.org/book/ch07-02-modules-and-use-to-control-scope-and-privacy.html 2108 | 2109 | 2110 | ## ANSI colors 2111 | 2112 | In C, ansi colors for terminal text output are typically done with the escape code 2113 | in octal format, such as printf("\033]0;31m test red"); In Rust, you can use 2114 | the unicode escape sequence, instead of 033 octal, we can use unicode with 001b hexadecimal: 2115 | 2116 | ```rust 2117 | fn main() { 2118 | println!("\u{001b}[0;31m{}", "test red"); 2119 | println!("\u{001b}[0;32m{}", "test green"); 2120 | println!("\u{001b}[0;33m{}", "test orange"); 2121 | println!("\u{001b}[0;34m{}", "test blue"); 2122 | println!("\u{001b}[0;35m{}", "test magenta"); 2123 | println!("\u{001b}[0;36m{}", "test cyan"); 2124 | println!("\u{001b}[0;37m{}", "test white"); 2125 | println!("\u{001b}[0;91m{}", "test bright red"); 2126 | println!("\u{001b}[0;92m{}", "test bright green"); 2127 | println!("\u{001b}[0;93m{}", "test yellow"); 2128 | println!("\u{001b}[0;94m{}", "test bright blue"); 2129 | println!("\u{001b}[0;95m{}", "test bright magenta"); 2130 | println!("\u{001b}[0;96m{}", "test bright cyan"); 2131 | println!("\u{001b}[0;97m{}", "test bright white"); 2132 | println!("\u{001b}[0m{}", "restored to default"); 2133 | } 2134 | ``` 2135 | 2136 | There are also several crates that do this. Search the web for additional ANSI color features. 2137 | 2138 | ## Thanks 2139 | 2140 | Based on a8m's go-lang-cheat-sheet, https://github.com/a8m/go-lang-cheat-sheet, and 2141 | 2142 | - rust-lang.org, Rust book, https://doc.rust-lang.org/book/second-edition 2143 | - rust-lang.org, Rust reference, https://doc.rust-lang.org 2144 | - rust-lang.org, Rust by example, https://doc.rust-lang.org/rust-by-example/ 2145 | - rust playground, from integer32, https://play.integer32.com/ 2146 | - Phil Opp builds an OS in rust, https://os.phil-opp.com/unit-testing/ 2147 | - Rust Cookbook, Language Nursery https://rust-lang-nursery.github.io/rust-cookbook/algorithms/sorting.html 2148 | - Itertools docs https://docs.rs/itertools/*/itertools/trait.Itertools.html for more details 2149 | - carols10cent's js-rust cheatsheet, https://gist.github.com/carols10cents/65f5744b9099eb1c3a6f 2150 | - c0g https://stackoverflow.com/questions/29483365/what-is-the-syntax-for-a-multiline-string-literal 2151 | - codngame https://www.codingame.com/playgrounds/365/getting-started-with-rust/primitive-data-types 2152 | - Adam Leventhal post here, http://dtrace.org/blogs/ahl/2015/06/22/first-rust-program-pain/ 2153 | - Shepmaster, https://stackoverflow.com/questions/33133882/fileopen-panics-when-file-doesnt-exist 2154 | - again, https://stackoverflow.com/questions/32381414/converting-a-hexadecimal-string-to-a-decimal-integer 2155 | - user4815162342, https://stackoverflow.com/questions/26836488/how-to-sort-a-vector-in-rust 2156 | - mbrubek, https://www.reddit.com/r/rust/comments/3fg0xr/how_do_i_find_the_max_value_in_a_vecf64/ 2157 | - https://stackoverflow.com/questions/19671845/how-can-i-generate-a-random-number-within-a-range-in-rust 2158 | - Amir Shrestha https://amirkoblog.wordpress.com/2018/07/05/calling-native-c-code-from-rust/ 2159 | - Julia Evans https://jvns.ca/blog/2016/01/18/calling-c-from-rust/ 2160 | - huon https://stackoverflow.com/questions/23850486/how-do-i-convert-a-string-into-a-vector-of-bytes-in-rust 2161 | - EvilTak https://stackoverflow.com/questions/43176841/how-to-access-the-element-at-variable-index-of-a-tuple 2162 | - https://www.90daykorean.com/korean-proverbs-sayings/ 2163 | - oli_obk https://stackoverflow.com/questions/30186037/how-can-i-read-a-single-line-from-stdin 2164 | - ogeon https://users.rust-lang.org/t/how-to-get-a-substring-of-a-string/1351 2165 | - A.R https://stackoverflow.com/questions/54472982/converting-a-vector-of-integers-to-and-from-bytes-in-rust 2166 | - dten https://stackoverflow.com/questions/25060583/what-is-the-preferred-way-to-byte-swap-values-in-rust 2167 | - How To Rust-doc https://brson.github.io/2012/04/14/how-to-rustdoc 2168 | - Pavel Strakhov https://stackoverflow.com/questions/40030551/how-to-decode-and-encode-a-float-in-rust 2169 | - Zargony https://stackoverflow.com/questions/19650265/is-there-a-faster-shorter-way-to-initialize-variables-in-a-rust-struct/19653453#19653453 2170 | - Wesley Wiser https://stackoverflow.com/questions/41510424/most-idiomatic-way-to-create-a-default-struct 2171 | - u/excaliburhissheath and u/connorcpu https://www.reddit.com/r/rust/comments/30k4k4/is_it_possible_to_modify_wrapped 2172 | - Simson https://stackoverflow.com/questions/54472982/how-to-convert-vector-of-integers-to-and-from-bytes 2173 | - Raul Jordan https://rauljordan.com/rust-concepts-i-wish-i-learned-earlier/ 2174 | - Will Crichton https://www.youtube.com/watch?v=bnnacleqg6k 2175 | - Jimmy Hartzell https://www.thecodedmessage.com/posts/oop-2-polymorphism/ 2176 | - Peter Hall https://stackoverflow.com/questions/63437935/in-rust-how-do-i-create-a-mutable-iterator 2177 | --------------------------------------------------------------------------------