Week 09 / 2023

Mohamed Saif published on
9 min, 1765 words

Shell

  • xargs, very useful utility. Xargs is a Linux utility that accepts entries from its input and executes the command you specify for each entry.

Docker

  • stop all running container: docker stop $(docker ps -aq), or docker ps -aq | xargs docker stop

Rust

1
  • using latest version of rust: 1.67.1
  • cargo init creates a new cargo package. cargo run runs the package (it decided to compile the code in debug mode on your behalf to provide maximal error information).
  • Rust’s cargo tool provides both a build system and a package manager.
  • Cargo.toml describes the project’s metadata, such as the project’s name, its version, and its dependencies.
  • Assignment in Rust, more properly called variable binding, uses the let keyword: let name = "Rust";
  • Strings are guaranteed to be encoded as UTF-8. This means that you can use non-English languages with relative ease.
  • println! is a macro that prints a string to the screen. The exclamation point indicates that this is a macro rather than a function.
  • println! macro prints its arguments to standard out (stdout), whereas eprintln! prints to standard error (stderr).
  • Macros are similar to functions except that instead of returning data, these return code. Macros are often used to simplify common patterns.
  • comments in Rust are written with // and /* */
  • The {} placeholder tells Rust to use a programmer-defined method to represent the value as a string rather than the default representation available with {:?}.
  • Rust is not object-oriented.
  • Rust’s distinguishing feature as a programming language is its ability to prevent invalid data access at compile time.
  • safety, productivity, and control.
  • println!("record: {:?}", record); prints the record as a string:
records: Lines(
    Map {
        iter:
            SplitTerminator(
                SplitInternal {
                    start: 0, end: 119, matcher:
                        CharSearcher {
                            haystack: "common name,length (cm)\n Little penguin,33\n Yellow-eyed penguin,65\n Fiordland penguin,60\n Invalid,data\n ", finger: 0, finger_back: 119, needle: '\n', utf8_size: 1, utf8_encoded: [10, 0, 0, 0]
                        },
                        allow_trailing_empty: false, finished: false
                }
            )
    }
)
  • cargo run --release -q runs the code in release mode, which compiles the code to run as fast as possible. the -q flag suppresses the output of the compiler.
  • Because the scheduling of threads is determined by the OS rather than the program, it’s impossible to know if the thread defined first will be the one that runs first
  • Dangling pointers
  • Data races
  • Buffer overflows
  • Iterator invalidation
  • The result of assignment is a blank type, (), which is called the unit type. The unit type is used when there is no other meaningful return value.
  • At first glance, cargo is a front end for rustc, the Rust compiler, but cargo provides several additional.
  • fine-grained control over how data structures are laid out in memory and their access patterns.
  • ..multiple ways to create integers.
  • Performance, Concurrency, and Memory efficiency.
  • Rust does not rely on a garbage collector to provide its memory safety.
  • The Rust community prefers a bigger language with a compiler that does more, rather than a simpler language where the compiler does less.
  • data-oriented design is a programming paradigm that emphasizes the importance of data structures and their layout in memory.
  • In Rust, it is difficult to model cyclic data like an arbitrary graph structure.
  • Rust is slower at compiling code than its peer languages.
  • The compiler is strict, but helpful.
  • Rust is large! It has a rich type system, several dozen keywords, and includes some features that are unavailable in other languages.
  • ..many types are zero-sized. That is, these only exist as hints to the compiler and take up no memory at all in the running program.
  • Utilities written in Rust are compiled as static binaries by default. This compilation method avoids depending on shared libraries that you must install before the pro- gram can run. Creating programs that can run without installation steps makes these easy to distribute.
  • No Rust 2.0—Rust code written today will always compile with a future Rust compiler. It wll never release an new major version.
  • With Rust, there are three main command_line tools to learn: – cargo, which manages a whole crate – rustup, which manages Rust installations – rustc, which manages compilation of Rust source code
2
  • The compiler’s role is to translate the source code into machine code, as well as take care of lots of bookkeeping to satisfy the oper- ating system (OS) and CPU that it is a runnable program.
  • compiling with rustc: ructc main.rs
  • using cargo: one difference from rustc is that compiled executables are found in a <project>/target subdirectory.
  • cargo run -v:
`rustc
    --crate-name tfd_rust
    --edition=2021
    src/main.rs
    --error-format=json
    --json=diagnostic-rendered-ansi,artifacts,future-incompat
    --crate-type bin
    --emit=dep-info,link
    -C embed-bitcode=no
    -C debuginfo=2
    -C metadata=950f7406b43c5e79
    -C extra-filename=-950f7406b43c5e79
    --out-dir /media/data/open-source/tfd-rust/target/debug/deps
    -C incremental=/media/data/open-source/tfd-rust/target/debug/incremental
    -L dependency=/media/data/open-source/tfd-rust/target/debug/deps`
  • #[warn(dead_code)] on by default
  • Type declarations are required when defining functions.
  • The entry point to all Rust programs is main(). takes no args, and returns no value.
  • Variables are immutable by default, meaning that they are read-only rather than read-write.
  • println!() is a macro, which is function-like but returns code rather than values. When printing to the console, every input data type has its own way of being represented as a text string. println!() takes care of figuring out the exact methods to call on its arguments.
  • Strings: ""
  • Char: ''
  • Underscores (in numeric values) increase readability and are ignored by the compiler: let million: i64 = 1_000_000;
  • Floating-point literals without an explicit type annotation become 32-bit or 64-bit, depending on context.
  • Scalar (single) number types: i[8,16,32,64], u[8,16,32,64], f[32,64], isize/usize
  • Floating-point types (f) represent real numbers with special bit patterns to represent infinity, negative infinity, and “not a number” values.
  • prevent comparisons between types.
  • as operator for casting. It is safest to cast the smaller type to a larger one (promotion)
  • In the meantime, consider a trait as a collection of methods.
  • If you are from an object-oriented background, traits can be thought of as abstract classes or interfaces?
  • use std::convert::TryInto; let b_ = b.try_into().unwrap(); if a < b_ {} try_into() returns a Result (It can contain either a success value or an error value) type that provides access to the conversion attempt. note thay a is of type i32, while b is a u8 variable. The unwrap() method can handle the success value and returns the value of b as an i32 here.
  • Unlike integers, floating-point types have some values that do not play well together (by design).
  • Floating-point numbers are implemented by computing systems that use binary (base 2) mathematics, but are often asked to perform operations on decimal (base 10) numbers. This poses a problem because many values we care about, such as 0.1, have no exact representation in binary.
  • run with RUST_BACKTRACE=1 environment variable to display a backtrace.
  • Generally speaking, it is safer to test whether mathematical operations fall within an acceptable margin of their true mathematical result. This margin is often referred to as the epsilon. f32::EPSILON and f64::EPSILON?
  • Rust’s standard library is comparatively slim.
  • Crates are Rust’s name for packages.
  • Two forms of initializing non-primitive data types. One is a literal syntax available as part of the Rust language. The other is the new() static method, which is implemented by convention only and isn’t defined as part of the language
  • As an implementation detail, Rust’s for loop construct is expanded to method calls by the compiler.
  • the exclusive range syntax (n..m) and the inclusive range syntax (n..=m)
  • for i in 0..collection.len() Legal rust. However, it is generally discouraged.
  • use std::time::{Duration, Instant}; It brings the Duration and Instant types from std::time into local scope.
  • loop {...} continues to execute until a break keyword is encountered or the program is terminated from the outside.
  • A loop label is an identifier prefixed with an apostrophe ('): 'outer: for x in 0.. {... break outer;}
  • loop lables like goto?!: One place where it is still commonly used, though, is to jump to and clean up a section of a function when an error condition is detected. Use loop labels to enable that pattern.
  • Rust has no concept of “truthy” or “falsey” types. The only value that can be used for true is true, and for false, use false.
  • Rust is an expression-based language In programming languages from this heritage, all expressions return values and almost everything is an expression. This heritage reveals itself through some constructs that are not legal in other languages
  • assign variables from conditional expressions: let v = if [cond] {..} else {..} or use match.
  • Perhaps most surprisingly, the break keyword also returns a value. This can be used to allow “infinite” loops to return values.
  • match warns you if you haven’t considered a relevant alternative, compared to regular if/else.
  • Additionally, a match does not “fall through” to the next option by default. Instead, match returns immediately when a match is found.
  • A reference is a value that stands in place for another value.
  • References are created with the reference operator (&) and dereferencing occurs with the dereference operator (*).
  • These operators act as unary operators, meaning that these only take one operand. One of the limitations of source code written in ASCII text is that multiplication and dereferencing use the same symbol.
  • fn add_with_lifetimes<'a, 'b>(i: &'a i32, j: &'b i32) -> i32 {*i + *j}: <'a, 'b> declares two lifetime variables, 'a and 'b, within the scope of. parameter i/j is a reference to an i32 with lifetime a/b.
  • Underpinning Rust’s safety checks is a lifetime system that verifies that all attempts to access data are valid.
  • All values bound to a given lifetime must live as long as the last access to any value bound to that lifetime.
  • Capital letters in place of a type indicate a generic type: <T>
  • All of Rust’s operators, including addition, are defined within traits.
  • What is a trait? A trait is a language feature that is analogous to an interface, proto- col, or contract. If you have a background in object-oriented programming, consider a trait to be an abstract base class. If you have a background in functional program- ming, Rust’s traits are close to Haskell’s type classes. For now, it’s enough to say that traits enable types to advertise that they are using common behavior.
  • To reiterate: all of Rust’s operators are syntactic sugar for a trait’s methods.