Skip to content

Latest commit

 

History

History
2163 lines (1393 loc) · 67.1 KB

journal.md

File metadata and controls

2163 lines (1393 loc) · 67.1 KB

Learning journal.

https://doc.rust-lang.org/book/ch01-02-hello-world.html

Sept/23/2023

  • rustc is the compiler that generates a ready to run output.

    it generates an executable that you can use to run the code. Automatic linker.

  • println! is a macro.

    Rust doesn't accept functions with variable arguments.

    A macro can be used to parse variable arguments, either by invoking a function or by parsing the variable arguments itself?

    BABY STEPS: look into it later.

  • cargo new hello_cargo to create a new project.

  • cargo has a lot of options, and a special search system cargo search

Sept/29/2023 06:41

https://doc.rust-lang.org/book/ch01-03-hello-cargo.html

  • cargo build to build the project. It will got to the target dir.

  • cargo build --release to build the project in "release mode".

I've found no difference between the binary in debug and release mode.

The runnable binary is in target/release

The target/debug has the cargo build output

Cargo.toml is where the package definition is.

It is similar to package.json in nodeJS

And Cargo.lock "Seems to be the same, but it isn't":

Cargo.toml = floating dependency : github repo X.

Cargo.lock = fixed dependency generated during build.

UPDATE 03/Oct/2023: - locked dependencies mean versions are frozen in time until you call cargo update

Cargo.toml

[package]
name = "learning_rust"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
regex = { git = "https://github.com/rust-lang/regex.git" }

Cargo.lock

Generated after the build

# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3

...
[[package]]
name = "aho-corasick"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab"
dependencies = [
 "memchr",
]

[[package]]
name = "regex"
version = "1.9.5"
source = "git+https://github.com/rust-lang/regex.git#27a25385c0bd1228716271668febc88bd8c74932"
dependencies = [
 "aho-corasick",
 "memchr",
 "regex-automata",
 "regex-syntax",
]

...

Cargo is POWERFUL: there is a whole document about it, including scripts to build more complex things (like gradle with his groovy script?)

https://doc.rust-lang.org/cargo/guide/index.html

A ".toml" is a format specification. Sort of same goals as yaml : https://toml.io/en/

Sept/29/2023 08:19

https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html

In a "beginner" level view, rust is not quite different than C (not C++. C) . The difference is Cargo, that is there to help you packaging (which is a very welcome improvement over the more esoteric failure prone makefile systems we typically see for projects in the C language).

Not needing to worry about a makefile to make a reliable build is very welcome, indeed.

QUESTION: What about that edition = "2021" inside the Cargo.toml file? I've just ran rustup update and I'm already using rust from 2023...

ANSWER: edition can be thought about an "specification format". edition says the Cargo file is using a format for 2021: it was not changed in 2023, so they are keeping it as it is.

QUESTION: Just created a main.rs file. And cargo doesn't seems to recognize it. ANSWER: MIND THE FILE TIMESTAMPS. If the file is older than the binary, it won't recompile it. (DUH).

Macro calls needs those { } things! println! "Hello world" will fail.

abpicoli@DESKTOP-EPFPMPH:/mnt/e/projetos/my_rust_journal/chapter3_guessing_game$ cargo run
   Compiling chapter3_guessing_game v0.1.0 (/mnt/e/projetos/my_rust_journal/chapter3_guessing_game)
error: expected one of `(`, `[`, or `{`, found `"Hello chapter 3"`
 --> src/main.rs:3:14
  |
3 |     println! "Hello chapter 3";
  |              ^^^^^^^^^^^^^^^^^ expected one of `(`, `[`, or `{`

SOLUTION:

fn main() {

    println! {"Hello chapter 3"} ;

}

Question: What is the difference between '(' , '[' or '{' when invoking a macro??

Answer: (hypothesis) : because a macro can be invoked over multiple object types: single stuff ("x"). vector stuff ["x"] or tuple stuff? {"x"}?

https://doc.rust-lang.org/reference/macros.html

Sept/30/2023 09:46

Initial evaluation: Can we use Saxon from RUST?

One of the demands of my work is xml processing. Our solution is based from the end user perspective (that is, someone developing an integration using the java framework we have) is a typical web-app configuration (web.xml) that takes a properties file that contains specifications of a sequence of "processors" to be invoked.

The glue specifying the order between processors and the data used is XML, specifically through XSL processing XML data.

So, if this platform is to be ported in some way to rust, without causing extensive re-learning of another language than the typical XSLT the end users are accustomed to, Rust must have something that supports XSLT. Preferably version 2 or 3.

The only tool in the market I know to support that is Saxonica's Saxon 12.

All xml libraries I've seen of rust are very incipient, not even supporting XSLT.

And I've discovered that Saxon doesn't support Rust.

Well. Rust supports C calls (grudgingly, dare I say?) and Saxon supports C programs. So maybe

https://www.saxonica.com/saxon-c/documentation12/index.html#!samples/samples_c this can be done eventually.

I'm not too worried about environment compatibilities now because the solutions tend to be run from inside a docker environment, with a fixed Ubuntu OS.

WELL::: BABY STEPS !

Resuming https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html

I've created the project inside 'chapter3_guessing_game' directory. And the insights I have are in multiple commits in code comments in the main.rs

Oct/1/2023 09:51

rust use ~= java import

In the last day, I did discover that what in java we call "packages" and the "import" directive are not that different (in a very first level view, mind that)

use std::io is not unlike import java.io.* in java. In the sense it is only an abbreviation of a dependency involving methods that may be invoked without a full package reference.

use std::io ... stdin.read

is equivalent to std::io::stdin.read

like with java import java.io ... InputStream i

is the equivalent of java.io.InputStream i

rust is not object oriented. Not in first view.

In java, even a main class is a class. the pattern is class X { public static void main(String... args) { ... } . Classes have constructors with specific invocations.

In rust, what we have are conventions: let mut x = String::new is not the same thing as StringBuilder x = new StringBuilder().

For starters:

  • the new in the rust code is a convention. I could call the constructor whatever I want. the new is only a method name commonly used to instantiate new instances.

BIG BIG BIG difference between java and rust!

In rust, libraries are provided for common functions, so in a sense there is an "equivalent" of the java standard API. And the java API is HUGE. java.io, java.util, java.text. java.sql ... And EVERYTHING is packaged as a single release. Java 1.8, java 9, java 10.

And for java, a java runtime must comes with every single package there as part of the package.

Not so with rust, it seems:

IN java, want to use the random generator? Just invoke

import java.util.Random 

...

public static void main(String... args) {
	Random x = new Random();
	x = Random.nextInt();
}

In rust, even something as trivial as random number generation is out, and it must be specified in the cargo.toml.

Well... rust is not unlike C, in this aspect right? To use rand we need to #include <stdlib.h>... But YES, it is different! Because the stdlib reference is FLOATING: it is a reference to whatever is installed into your own computer.

Like java. Or python with that PIP install thing (at least at first view. Not a python expert).

In rust, I must specify a library and a version . In the cargo.toml:

[dependencies]
rand = "0.8.5"

And only then can I use it inside the code.

UPDATE: When I've tried to simply qualify the reference to the code from inside the code, I've just discovered that rust crates are crazy!!!

If you have a code like


       let secret_number = rand::thread_rng().gen_range(1..=100);

Obviously I could use the full reference package, like, ignore the rand::Rng

and do something like this... Right? RIGHT?? WRONG!

    let secret_number = rand::Rng::rand::thread_rng().gen_range(1..=100);

error[E0223]: ambiguous associated type --> src/main.rs:13:25 | 13 | let secret_number = ::rand::thread_rng().gen_range(1..=100); | ^^^^^^^^^^^^^^^^^^^^^ | help: if there were a trait named Example with associated type rand implemented for dyn Rng, you could use the fully-qualified path

THERE IS MUCH MORE TO rust crates than only the java equivalent of "packaged classes"... Baby steps. More to come.

https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html#generating-a-random-number

03/Oct/2023

The more I look into it, the more I see that there is a big concern about packaging and documentation when publishing crates (which would be traditional java jars).

The use directive mention a crate and then something inside the crate.

"Remember that a crate is a collection of Rust source code files. " : no binary distribution, no .jar files. That is why we hear in the wild about rust having a slow build.

If you look into the target directory, there is a debug/deps directory, that contains a reference to your local registry: /home/abpicoli/.cargo/registry/

E:\projetos\my_rust_journal\chapter3_guessing_game\target\debug\deps\cfg_if-52fc72e823305762.rmeta: C:\Users\Usuario.cargo\registry\src\index.crates.io-6f17d22bba15001f\cfg-if-1.0.0\src\lib.rs

E:\projetos\my_rust_journal\chapter3_guessing_game\target\debug\deps\cfg_if-52fc72e823305762.d: C:\Users\Usuario.cargo\registry\src\index.crates.io-6f17d22bba15001f\cfg-if-1.0.0\src\lib.rs

C:\Users\Usuario.cargo\registry\src\index.crates.io-6f17d22bba15001f\cfg-if-1.0.0\src\lib.rs:

A CRATE IS A SOURCE-CODE DISTRIBUTION!!! COMPLETELY DIFFERENT FROM JAVA AND THEIR MAVEN COUNTERPARTS!!

Shadowing:

In java, you specify the type and the variable with that name has that type, forever, in the scope of the application.

In rust, you may have multiple versions of the same variable, with different types.

And the correct type is used depending on the context??? No, right? It seems the type is "shadowed" into a new type?

Yes, this is what happens: guess becomes a u32 variable through the rest of the code.

let mut guess = String::new(); ... let guess: u32 = guess.trim().parse().expect("Please type a number!")

Match:

match is a switch for enumerations.

But it is more... For example, the parse() method has an Ok(number) as an option, or an Err(error message) as other option, so you must specify the content, and can use the parameter as useful information on responding to each case.

	let guess: u32 = match guess.trim().parse() {
		Ok(num) => num, //just return the input number
		Err(err) => {
			println!("Bad number ! {err} {guess}"); // err =  the error message when parsing the input...
			continue;
		}
	};

Oct/4/2023

rustup doc has exactly the same documentation as the rust-lang site.

The rosetta stone for java and rust:

UPDATE from Oct/30. This effort is almost puerile... Rust have a totally different set of concerns in design. If you want to "translate" a java program into rust you must go deep, and probably refactor the application in so many ways to be able to run it under the mutability and borrowing constraints that you will need to write it from scratch, probably.

|RUST | JAVA| |let x = 5|final int x = 5| |let mut x = 5|int x = 5| |const x:i32 = 5| roughly private static final int x = 5 |

  • the const keyword represents a global variable inside a single rust file(? explanation from Oct/5th - BBABY STEPS.

  • Unlike java, where final variables may be initialized during class construction, CONSTS must be evaluated at compile time.

  • The let operator cannot be used outside a function. so, you can't quite specify (at first view from Oct/5th, BABY STEPS) a "class level" mutable variable that is used in multiple functions.

    So, at FIRST view, rust is quite close to a FUNCTIONAL|PROCEDURAL language? (More function than procedural, in fact...) BABY STEPS.

  • The file /ch3_1_variables_and_mutability/src/main.rs shows examples and comments regarding the usage of variables and constants in rust.

Rust and java are the same regarding scope:

You can do stuff like this in RUST:

fn main() {

	let x = 5;
	{
	
		let x = 6; // can do it, same variable, inner scope, like java and c.
		println! ("{x}") // 6
	}

}

RUST HAS SHADOWING IN THE SAME SCOPE!!!

As long as a variable is not MUTABLE, the let keyword allow you to redefine the variable with the same name multiple times.

UPDATE from Oct/30 me: shadowing can be applied anywhere. Variables being mutable or not. the let keyword specifically generates a new variable with the scope of "from now on in the code sequence".

let x = 1 loop { let x = x + 1; // valid println!("{x}") // valid: print 2

}

Oct/6/2023

Rust is similar to Java regarding types in the concept that types must be known at compile type.

A string parse method has this crazy signature on rust: pub fn parse<F>(&self) -> Result<F, <F as FromStr>::Err>where (very like java generics, btw)

If we place a variable

let x = "123".parse().expect("boom!") , both sides of the assignment are unknown at compile time. ERROR.

So, two solutions:

let x: u32 = "123".parse().expect("boom!")

or make other side of the equation have the desired type let x = "123".parse::<u32>().expect("boom!")

INTEGERS: if unspecified , assumes i32.

Can be i/u(8,16,32,64,128,size) isize / usize = architecture size : 32 bits for 32 bit machines, 64 for 64 bit machines.

Thousand separators with _

let x = 1_000 // i32 let x:u32 = 1_000 // u32. let x = 1_000

numeric expressions may have a type as well.

let x = 1_000u128 //

0x0f hex 0o0f octal 0b11110000 binary. 0b11_110_000 binary as well. the _ may appear anywhere: it is ignored.

RUST doesn't accept overflow (at least in debug mode).

error: literal out of range for `u8`
  --> src/main.rs:57:15
   |
57 |    let y:u8 = 10_000 ;
   |               ^^^^^^
   |
   = note: the literal `10_000` does not fit into the type `u8` whose range is `0..=255`
   = note: `#[deny(overflowing_literals)]` on by default
let mut y:u8 = 255 ; 
y = y + 1

thread 'main' panicked at 'attempt to add with overflow', src/main.rs:58:8
stack backtrace:
   0: rust_begin_unwind
             at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/std/src/panicking.rs:593:5
   1: core::panicking::panic_fmt
             at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/core/src/panicking.rs:67:14
   2: core::panicking::panic
             at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/core/src/panicking.rs:117:5
   3: ch3_1_variables_and_mutability::main
             at ./main.rs:58:8
   4: core::ops::function::FnOnce::call_once
             at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

characters are 4 bytes

let z:char='w';

+, -, *, /, % : all traditional arithmetic operations.

In RUST, CHARS ARE NOT INTEGERS: you can't cast a char into a integer directly and vice-versa.

There are no promotions and automatic conversions??

error[E0308]: mismatched types
  --> src/main.rs:84:13
   |
70 |     let mut theChar:char = iCode;
   |                     ---- expected due to this type
...
84 |         theChar = iCode;
   |                   ^^^^^ expected `char`, found `u32`

Oct/7/2023:

RUST is CRAZY! Even the lowest of the variables have "methods" assigned to them!

https://doc.rust-lang.org/std/primitive.char.html

With java, any operation like "is digit", would demand a different class, Character.

With Rust, all methods are assigned TO THE PRIMITIVE TYPE ITSELF!

Java:

char c = '1'; if (Character.isDigit(c)) ...

Rust:

let char c = '1' if (c.is_digit()) ...

RUST CASTS: casts are explicit. They DO exist.

let c = 'A' let cnum = c as u32

If you look at the char primitive type reference https://doc.rust-lang.org/std/primitive.char.html

You will see that what would in java be a primitive type with very little that can be done with it,

has a miriad of methods that can be used.

Match is an expression - it is a "functional" case:

let the_char:char = match char::from_u32(i_code) {
			Some(x) => x,
			None => '·'
		}; 

Function pointers do exist.

As we browse the primitive types standard library, we see that the function is a pointer. We can specify variables that are functions.

A tuple is a function signature without a function.

let tup: (u32,i32) = (123,124)

let (x,y) = tup;

// x = 123, y = 124

Tupple is also a sort of "array" : items can be accessed by index.

assert! ( tup.0 == x) // true

TUPLES can be heterogeneous. ARRAYS must be homogeneous.

As all rust variables tuples and arrays are BOTH immutable unless explicitly set with the mut keyword.

UNLIKE JAVA, where arrays are implicitly mutable and can't be ever locked, unless we wrap the array around a method that would return a "read-only" copy of the array.

Composite types are LIMITED: tuple and array. DONE?

Statements vs expressions is a HUGE concept.

At first glance, the talk about statements and expressions found in topic 3.5 in the rust language manual are somewhat moot.

But then we see stuff like

"if is an expression".

POWERFUL!

In java, if we want to make an assigment to a variable depending on a flag can be done either by a very lengthy if

JAVA:

int value;
boolean condition = true;

if(condition) { 
   value = 3 ; 
 }
else {
  value = 4;
}

And the alternative syntax is the plain old confusing ? : statement

value = condition?3:4;

That will need to be refactore into the previous if if there is any more complex decision taking in the code.

IN rust. Because if is an EXPRESSION, I can simply

let value = if condition { 3 } else { 4 }

And if I have extra conditions, I can keep concatenating this, forever...

let value = if condition { if condition2 { 42 } else { 3} } else { 4}

And of course I can make this structured, multiline, simple to read.

GUESS WHAT? LOOP is also an expression...

let result = loop {
        counter += 1;

        if counter == 10 {
            break counter * 2;
        }
    };

BREAK can return an expression, making loop an expression as well.

RUST: break labels must have a single quote

'outer: loop {
   'inner: loop { 
   ...
	break 'outer;
	...

LOOP can be a dynamic expression. WHILE can't . it returns always () .https://doc.rust-lang.org/std/keyword.while.html

FORS are iterator looping.. ALWAYS.

in java we can do something like this : for(int i=0; i < 100; ++i) ...

or this: List x = new ArrayList<>(); ....

for( k : x ) // iterates over the items in the list.

RUST ONLY CONTAINS THE LATTER: loop over an iterable thing.

08/10/2023 - Understanding ownership

Rust deals with the heap in principle much alike java:

Two variables that demand dynamic data will have the bulk content still going into the heap.

let s1= String::from("Hello") => pointer + some metainfo goes to stack. "Hello" goes to the heap as a sequence of characters. let s2 = s1 => s2 pointer (pointing to s1 "Hello") + some metainfo goes to stack.

YOU CAN'T ASSIGN TWO VARIABLES TO THE SAME POINTER IN THE HEAP (EXCEPT in READ-ONLY ACCESS)

The latest assignment borrows the pointer making the previous variable no longer valid.

So, for functions, you may add the function as a parameter AND THEN RETURN THE SAME VARIABLE to the caller, so the caller can 'take it back'.

THAT IS WHY println! must not be a simple function! if I was to invoke a println function, I would need to invoke something like (pseudocode)

let mut tup: (String,String) = ("Hello","World");

// would have to do that to keep tup in my own scope. At the moment I place I call a function with heap parameters, // I lose them.

tup=println("The value of {} is {}",tup);

Any type can "bypass" borrowing by implementing the Copy trait (whatever that is):

This would make a copy of the variable before sending the function, passing the full content "by value".

TO AVOID THAT IN FUNCTION CALLING, References are used.

TODO: I have started a project to experiment on all this concepts, but haven't done anything yet.

A call by reference '&' can be used for READ-ONLY access to a variable.

09/10/2023: Ownership experiments.

The project borrow_experiment is a work in progress trying many stuff with borrowing and inferring types.

From that we learned that "Hello" is not a string, but an '&str' .

There is more inside the code.

If something is immutable in Rust, It IS immutable.

The borrow checker makes sure that an immutable thing is KEPT as immutable, and can't be referenced later after change.

error[E0502]: cannot borrow `the_mutable_string` as mutable because it is also borrowed as immutable
   --> src/main.rs:123:2
    |
122 |     let mutable_alias = &the_mutable_string ;
    |                         ------------------- immutable borrow occurs here
123 |     the_mutable_string.push_str("Olá!");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
124 |     println!("the_mutable_string = {the_mutable_string} ; mutable_alias = {mutable_alias}");
    |                                                                           --------------- immutable borrow later used here

10/10/2023: More ownership experiments - Structures

The borrow checker experiment went to my satisfaction for the moment. Moving to next chapter.

10/11/2023: A new update?

Well... "just" discovered that the toolchain was updated on Oct/3/2023.

Now, let's go back to structs. I'm playing with the concept of structures with a probability game:

This is common in multiple games and simulation scenarios: there is a probability of an event to occur, and then a random event is chosen based on that probability table.

So there can be a structure like this:

probability event
0.01 "A whale falls from the sky and crashes over you. You lose (0-1000) hitpoints"
5.00 "As you walk through the forest, you find a brand new sword and shield! "
25.0 "A pack of giant rats attacks you. Prepare for an encounter!"

Probability is a number that shows the relative chance of that event occurring comparing to the others.

That is, the chance of finding a sword and shield is (5.00 / 0.01 = 500 ) times more likely to happen than a whale falling from the sky.

This is what I've got so far. Tomorrow I will try to implement this with my current knowledge.

Oct/12/2023: my initial attempt with structures.

I'm committing the initial code for my structure experiment. Given a probability array as input, A table containing odds of a specific event to happen is printed.

This have not gone through the cargo run yet, so I will watch the fireworks! :)

Immutability implies in making transformation functions.

In the code, I had an array of events, and I would use a for x in 0..events.len() to change the array, (making it mutable), but I've decided not to: the array have a map function, that returns an array transformed by a function.

UPDATE: this commit fix all the issues and place some comments:

General hints:

  • Use references for 'for loops'. Borrowship will take your variable and not return it in a for loop.

    So: for f in some_var { // probably wrong: it will remove your variable and never return it.

    for f in &some_var { // good: it will keep the original reference after the loop is completed.

  • REMEMBER: rust makes NO PROMOTIONS; There are no implicit return types, there are no implicit conversions from integer to floating.

  • RUST is dumb regarding deducting types: it will only detect them for the most simple of assignments. Make all types explicit.

Oct/13/2023: structs are classes... in a way.

Considering that you can write code like this:

struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}

all the elements are there: I have the data, and the methods I can use to work with that data.

What remains to be seen is how to implement inheritance, polymorphism, etc. Smells that "traits" will do that.

But even in chapter 5, there is this idea that I can make an implementation that returns "Self". That is, a "don't bother with the class name, just do this."

impl Event {

	fn new(odds: f64, text: &str) -> Self {
		Self {
			odds,
			text: String::from(text),
			min:-1.0,
			max:-1.0
		}
	}

}

Why would the language allow that concept, if not to implement some sort of polymorphism, where I can run this new thing to "anything that makes noise", like in the traditional polymorphism examples?

Another question is "what about public , protected, private?"

Well, let's go to chapter 6.

enum DEFINITELY is not our java enum.

https://www.youtube.com/watch?v=ybrQvs4x0Ps the billion dollar mistake.

https://en.wikipedia.org/wiki/Tony_Hoare.

Oct/14/2023: some hangs trying to work with the Option enum:

Today I have finished watching the Tony Hoare meeting about the billion dollar mistake.

"The idea of using "proof" : https://youtu.be/ybrQvs4x0Ps?t=1900 appears in the middle, where the guy seems to talk about Test Driven Development

The project ch6_enums is a work in progress for an "adventure like" game.

But I'm dealing with some hangs regarding the use of Option with the function pointer type. WIP.

Oct/15/2023: let me just move on:

The ch6 project clearly seems to show that I still don't know enough to make the "adventure game".

There are a lot of things that make me suspect the borrow checker will vanish with the data and not use it.

From chapter 7: "A package can contain multiple binary crates and optionally one library crate" ?

The exercise for chapter 7 : turn the ch6 project into a functional project, where each scene and options are inside a module.

Oct/16/2023: Rust pushes functional. Because mutable is hard to handle.

In chapter 6 exercise we got a lot of trouble with mutable variables (choosing lifetimes and such).

But the point is that, at least for this adventure example, we don't need it: I can use "normal" variables and design the application to be heavily functional.

Because functions appear not to be affected by the borrow checker . After all they are code: intrinsically static discovered at compile time. Right? Right??

Hm... demands an experiment.

So, let's think about the structures.

The game is a simple text-based adventure.

All the player can do is to choose between up to 3 choices.

The player is in a scene, that describes what the player is seeing. What he sees will depend on the player history.

The player has Tags that shows details about the game progress.

For example, on chapter 6, the player starts with the NAKED tag.

When we talk about choices, I have two concerns:

  1. Is the choice provided applicable based on the scene and player?
  2. Once we apply the choice, we provide player and scene, and receive a new player and scene.

So a player has tags. To make it simple, tags will be simple strings, separated by semicolons;

Some helper methods may be used to ask if a player has a tag has_tag

Remove a tag remove_tag;

and add_tag;

a pretty_print tag will be used to "pretty print" the possessions. In the beginning it will be only a simple string.

The scene will have

describe(&Player) : providing descriptions based on player tags.

choices: an array of 3 Choice elements.

A choice will have:

fn is_applicable(Scene,Player) -> boolean :

fn apply(Scene,Player) -> (Scene,Player)

fn describe(Player) -> String : describes the scene to the player.

NOTE: I'm also looking at chapter 11 (tests) : I can't possibly go on without some test driven development :D

Day close: as expected, even the simplest code has lots of fireworks :(

Committing code at state: it is not even compiling yet.

Oct/17/2023: Rust IS A PAIN IN THE ASSSSSSS!!!!!!!!!!!!!!!!

In any language in the WORLD...

return a string concatenated with other string would be a simple concatenation operation. One liner.

So, this function which is a one liner in java, can be written in rust, right? RIGHT??

pub fn format_tag(tag:&str) {
	"♦ " + tag + "\n" 
	
}

NOT SO WITH RUST:

abpicoli@DESKTOP-EPFPMPH:/mnt/e/projetos/my_rust_journal/ch7_modules$ cargo test player.rs Compiling ch7_modules v0.1.0 (/mnt/e/projetos/my_rust_journal/ch7_modules) error[E0369]: cannot add &str to &str --> src/player.rs:13:7 | 13 | "♦ " + tag + "\n" | ---- ^ --- &str | | | | | + cannot be used to concatenate two &str strings | &str | = note: string concatenation requires an owned String on the left help: create an owned String from a string reference | 13 | "♦ ".to_owned() + tag + "\n" | +++++++++++

So, let's satisfy the rust compiler. Let's make a String::from in the first content:

pub fn format_tag(tag:&str)->String {
	String::from("♦ ") + tag + "\n" 
	
}

This have returned a string. And as consequences, the raw data from the string is useless for many operations, such as an replace operation.

So, all over the code, I need to do stunts like this:

format_tag(NAKED).to_owned()+&format_tag(RAGGED_CLOTHING)

Well... tomorrow I will work with the other modules.

Oct/18/2023:

Hm... yesterday I did this stunt. Probably because I was crazy with the str x string confusion.

	pub fn add_tag(self,tag:&str) -> Player {
		if ! self.contains_tag(tag) {
			let mut x:String = self.tags.clone();
			x.push_str(&format_tag(tag));
			return Player {
				tags:x.clone()
			}
		}
		self
	}

But... I think this is legal, and much cleaner, right?

	pub fn add_tag(self,tag:&str) -> Player {
		if ! self.contains_tag(tag) {
			let x = self.tags.clone() + &format_tag(tag);
			return Player {
				tags:x
			}
		}
		self
	}

What does the operator "add" does to a string?

And Rust documentation does have the answer: https://doc.rust-lang.org/std/string/struct.String.html#impl-Add%3C%26str%3E-for-String

The problem with concatenation is that it will always "consume" the left operator. x + y = x.add(y) , which implies a call to x(self,y) .

Well... just compiled the change in player.rs and... no issues. Ok.

AND... in the end the big problem is that I'm probably doing things the complicated way.

I bet the macro concat! https://doc.rust-lang.org/stable/std/macro.concat.html will be much more efficient,

and simpler to use.

Let's try it:

from

pub fn format_tag(tag:&str)->String{
	let x = String::from("♦ ") + tag + "\n" ;
	x
}

to

pub fn format_tag(tag:&str)->String{
	String::from(concat!("♦ ",tag,"\n"))
}

There is no happiness :(

error: expected a literal
  --> src/player.rs:13:28
   |
13 |     String::from(concat!("♦ ",tag,"\n"))
   |                               ^^^
   |
   = note: only literals (like `"foo"`, `-42` and `3.14`) can be passed to `concat!()`

e

and guess what what the add operator is , behind the covers??

https://doc.rust-lang.org/src/alloc/string.rs.html#2265
 fn add(mut self, other: &str) -> String {
        self.push_str(other);
        self
    }

So, specially for multiple c, it may be better to simply push_str. Avoid a stack call

Oct/19/2023

I'm working on the choice.rs today. Because a scene needs a choice.

A choice

A choice will have:

fn is_applicable(Scene,Player) -> boolean :

fn apply(Scene,Player) -> (Scene,Player)

fn describe(Player) -> String : describes the choice to the player.

The choice seems to be a set of functions, right? Smells like these "Traits" concept?

Well... let's just try making the structure, made of function pointers.

Ans THIS is the suspense point: I'm committing this code. Will it compile?

abpicoli@DESKTOP-EPFPMPH:/mnt/e/projetos/my_rust_journal/ch7_modules$ cargo run
   Compiling ch7_modules v0.1.0 (/mnt/e/projetos/my_rust_journal/ch7_modules)
warning: fields `is_applicable`, `apply`, and `describe` are never read
 --> src/choice.rs:6:2
  |
4 | pub struct Choice {
  |            ------ fields in this struct
5 |
6 |     is_applicable: fn (&Scene,&Player) -> bool,
  |     ^^^^^^^^^^^^^
7 |     apply: fn(Scene,Player)->(Scene,Player),
  |     ^^^^^
8 |     describe: fn(&Player) -> String
  |     ^^^^^^^^
  |
  = note: `#[warn(dead_code)]` on by default

warning: `ch7_modules` (lib) generated 1 warning
    Finished dev [unoptimized + debuginfo] target(s) in 3.51s
     Running `target/debug/ch7_modules`
Hello, world!

Yep it did.

Now, how do I create multiple choices? Probably with a constructor... Because I don't think I can use "const" for declaring a choice.

Well, more for tomorrow.

Oct/20/2023:

Well... the idea of making a struct containing pointers to functions works... barely and awkwardly.

Sure, you can specify a struct containing pointers to functions.

But the function itself is hardcoded at compile time, so you can't create a struct with some "anonymous definition". You must explicitly point to an existing function in code, by name.

Also, invoking this function pointed by the struct is also awkward, having to place the field inside parenthesis ...

So, yes. Let's move to next chapter and skip this ch7 project.

Oct/21/2023:

Reading chapter 8 about collections.

And it seems the borrow checker attacks again...

let mut v = vec![1, 2, 3, 4, 5];

    let first = &v[0];

    v.push(6); // BOOOM!!!!

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

Because of the borrow checker system, vectors are implicitly synchronized! So the stunts we must do with java when having a vector changed in multiple threads (Concurrent modification exception) will happen at the very beginning of an operation...

Which means I must for sure use something else to allow multiple threads to modify the same vector without giving a compile time error, right??

More to see when we try async operations later.

BIG TAKEAWAY: STRINGS ARE COMPLICATED : Strings are a vector of characters, but to support all internationalization, diacritics, chinese characters, and so on, you can only iterate through the string, and not access a specific character by index.

FOR TOMORROW: make the tests for the exercise at the summary of chapter 8

Oct/23/2023:

Did take a look into the "Rust Drama".

Starting looking into the first exercise of chapter 8

Given a list of integers, use a vector and return the median (when sorted, the value in the middle position) and mode (the value that occurs most often; a hash map will be helpful here) of the list.

The list of integers will be provided by user input. A line containing numbers separated by spaces.

The project ch8_1_median contains the initial output of the exercise.

Oct/24/2023:

Made some strides in ch_8_1. Decided to order the steps in the main method as functions so they can be tested in isolation with multiple use cases.

This manual input is tedious.

Oct/25/2023:

Ended up looking at rust forums. So, yes, rust is much more like C++ than anything else regarding portability.

Just discovered the magical into() method that converts one type to another implicitly.

In the rust library there is this concept of "blanket implementations". Need to see that later.

First median test working.

Borrow check is a b*tch:

This doesn't work, because x was borrowed when placed into the first item.

(x,y)=(x,x[y].into())

  --> src/main.rs:45:23
   |
39 | pub fn median(the_vector:Vec<u32>) -> (Vec<u32>,f64) {
   |               ---------- move occurs because `the_vector` has type `Vec<u32>`, which does not implement the `Copy` trait
...
45 |         return (the_vector, the_vector[vec_len / 2 ].into());
   |                 ----------  ^^^^^^^^^^ value borrowed here after move
   |                 |
   |                 value moved here
   |

The solution? Assign the expression in the step before into a variable.

Oct/26/2023:

Keep working on the chapter 8: median.

A reinforcement on the mut keyword: the mut keyword declares INTENT. It says i want to change a variable.

Because of that it makes sense in variable declarations and function declarations.

It doesn't make sense on function results, because what people want to do with the outcome of a function is out of scope of the function.

Today I had to review once more chapter 4.

If you want a complex type to mutate in content inside the code, you must specify that the parameter is a mutable reference (&mut) and then specify when calling the function that you are passing a mutable reference to the variable.

So...

fn median(the_vector: &mut Vec) -> f64 { the_vector.sort_unstable(); // a mutable reference can be referred directly inside the function. No "&" or "*". }

...

x : mut Vec=vec![1,2,3,4,5];

let the_median = median( &mut x)

Oct/27/2023:

Completed exercise 8.1 - mode / median.

Starting exercise 8.2 - piglatin

Tried to find a library to check if a vowel is such in romance languages. Couldn't find any implementation that has an actually exhaustive value. So I will keep it simple, and only consider vowels "AEIOUaeiou"

Rust concerns with internationalization are a bitch .

char.to_lowercase is not a single char! It is a lowercase structure, that may contain more than one character. Crazy!

How I miss 1980s old 127 characters ascii :D .

Oct/28/2023:

How to run the debugger for tests (using Windows Ubuntu WSL).

  1. make sure you have gdb installed. (sudo apt-get update ; sudo apt-get gdb )

  2. the program rust-gdb allows to debug source code. The program to use to debug tests is rust-gdb.

The test output will show a line containing the binary that failed.

     Running unittests src/main.rs (target/debug/deps/ch8_2_piglatin-03717bc444e3dfba)

Once it's found, you can start rust-gdb with that file.

rust-gdb target/debug/deps/ch8_2_piglatin-03717bc444e3dfba

Borrow checkers are a pain in the ass! Again.

Oct/29/2023:

Work on exercise 8.3

Using a hash map and vectors, create a text interface to allow a user to add employee names to a department in a company. For example, “Add Sally to Engineering” or “Add Amir to Sales.” Then let the user retrieve a list of all people in a department or all people in the company by department, sorted alphabetically.

Well... it make sense to make a structure, right?

{ employee_id , department_id }

{ id , department } to have a list of departments.

{ id , employee } to have a list of employees.

So the user will have the following options:

add [new] "<<employee>>" to [new] "<<department>>" add an (optionally new) employee to an (optionally new) department.

Response: employee X (id) was added to depertment Y (id)

remove <<employee>> remove <<department>> list <<employee>> [ by_id ] list <<department>> [ by_id ] : list all departments in alphabetical order (or by id , if "by_id" is set ).`

The code will have a lexer to parse the text response.

I will start slow. With the lexer.

there will be a command structure:

{

enum operation {
	add(bool new_employee, employee,bool new_department,department) , remove(target,name) , list(target,by_id)
}

enum target {
	employee , department
}

}

It's sunday. let's leave for tomorrow.

Oct/30/2023:

Today I did review this test, and translated some portuguese notes into english. All english now.

I've also added the project 8_3_human_resources. But it is empty right now.

Oct/31/2023:

I will work with the "database module" today.

Nov/1st/2023:

Interesting issue found today:

abpicoli@DESKTOP-EPFPMPH:/mnt/e/projetos/my_rust_journal/ch8_3_human_resources$ cargo test
error: failed to parse manifest at `/mnt/e/projetos/my_rust_journal/Cargo.toml`

Caused by:
  no targets specified in the manifest
  either src/lib.rs, src/main.rs, a [lib] section, or [[bin]] section must be present
abpicoli@DESKTOP-EPFPMPH:/mnt/e/projetos/my_rust_journal/ch8_3_human_resources$

THe files mentioned are there

abpicoli@DESKTOP-EPFPMPH:/mnt/e/projetos/my_rust_journal/ch8_3_human_resources$ find . -type f
./Cargo.toml
./src/database.rs
./src/lib.rs
./src/main.rs
abpicoli@DESKTOP-EPFPMPH:/mnt/e/projetos/my_rust_journal/ch8_3_human_resources$

It seems that Cargo looks in the parent directory of the current project. In my case, the parent did have a ./Cargo.toml file, and it was complaining about non-existing targets.

I've opened a forum question to discuss this issue https://users.rust-lang.org/t/what-is-the-correlation-between-two-cargo-toml-in-the-file-hierarchy/101933/5

The quick solution is to make sure that the cargo.toml has the [workspace] tag so the application is not considered a component of a bigger one.

Nov/3rd/2023:

As I'm working in the database.rs, I've found this. I did see it before...

abpicoli@DESKTOP-EPFPMPH:/mnt/e/projetos/my_rust_journal/ch8_3_human_resources$ cargo test database.rs
   Compiling ch8_3_human_resources v0.1.0 (/mnt/e/projetos/my_rust_journal/ch8_3_human_resources)
error[E0106]: missing lifetime specifier
 --> src/database.rs:9:19
  |
9 |     employees: Table<&str>,
  |                      ^ expected named lifetime parameter
  |
help: consider introducing a named lifetime parameter
  |
7 ~ struct Database<'a> {
8 |
9 ~     employees: Table<&'a str>,
  |

I'm going to switch to chapter 10, that explains lifetimes.

Nov/4th/2023:

Even better... I will follow the sequence. Chapter 9 then 10.

panic! for big contract breaches.

Result for recoverable errors.

`bla()?.ble() can be used to concatenate operations that may throw results or options.

https://doc.rust-lang.org/reference/expressions/operator-expr.html#the-question-mark-operator

Generics in rust are probably faster than generics in java (and safer).

In Rust, the concrete type of a generic is defined at COMPILE TIME. Which means that there is no concerns of autoboxing and type checking we would have with java.

Leading to faster code.

On the other hand, because generics are defined at compile time, runtime linking to a dynamic library may be foolhardy. If even possible, at all.

Big chance rust will not have reflection features at all.

https://stackoverflow.com/questions/36416773/how-does-rust-implement-reflection .

So... lifetimes tells rust when borrowed references expire.

Borrow checkers are, as always, a B*TCH

After finally solving the database.rs issues with lifetimes, we got into a more fundamental problem:

We REALLY CAN'T have two hashmaps pointing to the same entity, right? The borrow checker doesn't allow that.

We can only store actual objects into the Hashmap. Not references to it.

So, the solution was: have ONE hashmap that refers to rows by ID.

Then have the hashmap by name referring to row IDs.

This will make both hashmaps to point to the same object, but one directly , the other indirectly (by id).

Nov/5th/2023:

Rust forums: today I've learned that shadowing doesn't free resources.

https://users.rust-lang.org/t/free-variables-after-last-usage/102004/28?u=abmpicoli

abmpicoli 35m

noob rust dev still learning the language.

But wouldn't shadowing var1 make it out of scope?

UPDATE: silly me, this was not the asked question 😃 . It was "could rust deallocate the variable automatically for me since I don't use it in code anymore".

I mean use std::fs; fn main() { // { let var1 = fs::read_to_string("16mb-text-file.txt").unwrap(); dbg!(&var1.len()); // } let var1 = 0 ; // would free the old var1, since it is shadowed, right? let var2 = fs::read_to_string("11mb-text-file.txt").unwrap(); dbg!(&var2.len()); }

Shadowing is about variable names. Internally rust makes all variables to be an anonymous number. So while the variable is not effectively out of scope, it is there, even after shadowing: it is just unaccessible.

There is a stack exchange topic about it :

https://stackoverflow.com/questions/45676411/why-does-shadowing-not-release-a-borrowed-reference/45676778#45676778

And in that topic there is something interesting about the compilation sequence:

t's not like the original r1 ceases to exist after it becomes shadowed; consider the MIR produced for your code without the last line (r2 binding):

What is MIR?? Found this rust article : https://blog.rust-lang.org/2016/04/19/MIR.html

It seems there is an option to show intermediate artifacts when compiling... need to know how.

Found it here: https://crates.io/crates/cargo-show-asm .

Summary of the day:

Discovered the rust impl train:

As we add more and more traits for some generic thing to work with rust, we end up creating stuff like this:

#[derive(Eq,PartialEq,Hash,Debug)]
struct Row<T:Display + Debug> {

...

struct Table<'a, T:Eq + PartialEq + Hash + Clone + Display + Debug> { 
...

impl<'b,T:Eq + PartialEq + Hash + Clone + Display +Debug > Table<'b,T>

...

}

Hm... it just dawned on me...

the java equivalent of a default interface method is the #[derive macro.

java:

public interface Something {

public default String doSomething() {
	return doThis(doThat());
	doThat();
}

public String doThis(String input) ;
public String doThat(String input);

}

RUST:

trait Something {

fn do_something() -> String ;
fn do_this(input:&String) -> String;
fn do_that(input:&String) -> String; 

}

// some procedural macro to implement do_something... BABY STEPS! Too confusing right now...

Nov/6th/2023 was used in the rust forums

https://users.rust-lang.org/t/rust-spec-and-implementations-opinions/102125/16

Nov/7th/2023:

Spent browsing forums.

NOv/8th/2023:

Thinking about the possibility of having some sort of discovered at runtime feature with rust.

This is the discussion why this is very difficult to do with rust.

https://faultlore.com/blah/swift-abi/#what-is-abi-stability-and-dynamic-linking

This api can be used to invoke java from rust. https://docs.rs/jni/latest/jni/

Nov/9th/2023:

One thing in the database.rs code I did for tests was to keep every single variable under the same scope.

What happens if the insertion operations happen within a scope? Will the test fail? YES! Miserably!

Because the reference to the string is dropped out of scope.

In short: we can't have references to &str inside a Hashmap, unless that hashmap has the same scope as the &str reference. Or shorter.

I need to keep copies of that data.

error[E0597]: `tommy1` does not live long enough
   --> src/database.rs:120:33
    |
119 |             let tommy1 = String::from("Tommy");
    |                 ------ binding `tommy1` declared here
120 |             let value = the_table.insert(&tommy1).expect("Since this is an empty table, the first row should be accepted automatically");
    |                                          ^^^^^^^ borrowed value does not live long enough
121 |             assert_eq!(0,value);
122 |         };
    |         - `tommy1` dropped here while still borrowed
...
125 |             let response = the_table.insert(&tommy2);
    |                            --------- borrow later used here

Long story short: for this database to work, the hashmap values and keys must be, well, VALUES, not references.

The instances of &str were all replaced by "String".

And every insert and update involves CLONING the string. Which is awful.

Well... whatever.

At least in the end I've got a successful test (the error in the find is because the test suite is not finished yet)


warning: `ch8_3_human_resources` (lib test) generated 8 warnings (6 duplicates)
    Finished test [unoptimized + debuginfo] target(s) in 6.17s
     Running unittests src/lib.rs (target/debug/deps/ch8_3_human_resources-43b3fa6b17d5eb4a)

running 1 test
test database::tests::test_table ... FAILED

failures:

---- database::tests::test_table stdout ----
Value Tommy already exists in the database as row  0
thread 'database::tests::test_table' panicked at src/database.rs:133:9:
The find function should find Tommy under id 0
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    database::tests::test_table

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

Nov/9th/2023 part 2:

In the rust forums there was this idea of "leaking" a string reference.

https://users.rust-lang.org/t/str-vs-string-for-hashmap-key/102290/16?u=abmpicoli

Will this work with my table structure?

TO DO...

Nov/10th/2023:

Today I'm trying to change the database code to use static &str references. And see how that goes.

Change in database.rs : will using a &'static reference compile?

diff --git a/ch8_3_human_resources/src/database.rs b/ch8_3_human_resources/src/database.rs
index c7d20dc..a34fb21 100644
--- a/ch8_3_human_resources/src/database.rs
+++ b/ch8_3_human_resources/src/database.rs
@@ -25,8 +25,8 @@ impl Display for IdMapping {

 struct Database {

-       employees: Table<String>,
-       departments: Table<String>,
+       employees: Table<&'static str>,
+       departments: Table<&'static str>,
        employee_vs_department: Table<IdMapping>,
 }

well... it worked, rust accepted it

warning: `ch8_3_human_resources` (lib test) generated 8 warnings (6 duplicates)
    Finished test [unoptimized + debuginfo] target(s) in 9.63s
     Running unittests src/lib.rs (target/debug/deps/ch8_3_human_resources-43b3fa6b17d5eb4a)

running 1 test
test database::tests::test_table ... FAILED

failures:

---- database::tests::test_table stdout ----
Value Tommy already exists in the database as row  0
thread 'database::tests::test_table' panicked at src/database.rs:133:9:
The find function should find Tommy under id 0
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

And well... it works. Even if I use a lifetime other than static. This test returned a success.

	#[test]
	fn test_table_with_str<'a>() {
		let mut the_table: Table<&'a str> = Table::new("my_table");

		{ 
			let tommy1:&'a str = String::from("Tommy").leak();
			let value = the_table.insert(tommy1).expect("Since this is an empty table, the first row should be accepted automatically");
			assert_eq!(0,value);
		};
		{
			let tommy2:&'a str = String::from("Tommy").leak();
			let response = the_table.insert(tommy2);
			if let Err(ref msg) = response {
				println!("{}",msg);	
			}
			assert!(response.is_err(),"Adding a new row with the same name should return an error");
		};
		
	}

Cargo test -- --show-output for showing println content.

https://doc.rust-lang.org/std/borrow/enum.Cow.html

https://users.rust-lang.org/t/str-vs-string-for-hashmap-key/102290/24?u=abmpicoli

Cow can be used somehow. Pay attention to the tests.

Nov/11/2023:

I'm going to try and implement the missing table methods for the database.rs today.

Well... my database structure seems to be wrong.

A note on borrowing:

getting a value from a hashmap borrows the whole hashmap.

error[E0502]: cannot borrow `self.rows_by_id` as mutable because it is also borrowed as immutable
   --> src/database.rs:100:4
    |
99  |             let row = self.rows_by_id.get(&id).unwrap();
    |                       --------------- immutable borrow occurs here
100 |             self.rows_by_id.remove(&id);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
101 |             self.row_id_by_name.remove(&row.value);
    |                                        ---------- immutable borrow later used here

First thing I had to do is to get a mutable reference to self.rows_by_id:

	let idMap = &mut self.rows_by_id;

Second is that a get will make the map immutable, unless we clone the row returned.

if idMap.contains_key(&id) {
			let row = idMap.get(&id).unwrap().clone();

It always involves some sort of cloning ...

Nov/12/2023:

Today I did the specification of what the database must do (database.rs).

The code is not even compiled yet, but today is Sunday.

Nov/15/2023:

Trying to make the database.rs tests to go green.

...

As I'm working on the database methods... I see more and more that what I'm doing, creating a Table structure, and adding a Database that contains those tables is overengineering to an absurd extent.

All that I need is a HashMap between person and department, a department map (to avoid typos in departments) and a person map (to avoid typos in person names: adding a non-existing person to a department).

And strings seems to be better than &str in general... I keep leaking stuff, while with strings I can simply pass them by read-only references, and the string already implement the copy trait.

Well, for tomorrow.

Nov/16/2023:

https://users.rust-lang.org/t/store-generic-trait-implementations-in-hashmap/73953/2?u=abmpicoli

One of the most powerful features of Java is the idea that a Map is a generic interface.

So, one can specify that a type accepts a Map. And that's it.

This seems to be not possible at all in rust, unless this "Box" thing is used.

Big bummer.

For tomorrow: Chapter12

Nov/18/2023:

Today I've discovered an experiment in the Brown Computer Science College about rewriting the Rust book.

https://rust-book.cs.brown.edu/

I've gone through chapters 1 and 2 there.

Nov/19/2023:

Spent my time in the Rust forum. Where I've discovered that cargo doc documents everything: every single dependent package of your project.

Which may cause some projects to inadvertedly become huge and slow to build.

A cargo doc --no-deps avoids this behavior.

https://users.rust-lang.org/t/why-so-many-files/102772 the discussion about this.

rust-lang/cargo#12998 (comment) there was a reply about my issue with the lack of the [workspace] tag when generating cargo.toml files with cargo new

Nov/24/2023:

The rust forum is giving me burnout and is distracting me from my goal, that is to learn rust in the first place.

So, I will change my approach: I will still use the brown.edu version of the rust book, but I will go back to chapter 12.

Nov/28/2023:

I've got vacation burnout. Had to drop studies.

But I'm changing strategy: I'm going to skim through the whole book. Once that is done, I will get back to chapter 12 and then try to work with the grep concept. I have this feel that I can't get deep enough without have at least a basic understanding of the language features.

The background questions and doubts about the benefits of learning the language itself are still nagging at me, big time.

13.1 - closures. Let's play with some closures.

A closure is a variable that contains a function. The type of a closure is a Fn(args)

Let's follow the train:

A closure starts with a closure specification and a return type:

let mut x = | y:i32 | -> i32 { 1 + 1 }

The part | y:i32 | -> i32 is the closure signature: input parameters and outputs.

This got me thinking: "Hey! Finally I will be able to introduce some dynamism to this language!"... PEEEM!!!! WRONG!!! THINK AGAIN!!!!

Because THIS doesn't work:

    let z = 42;
    let mut x = |y:i32 | -> i32 { z+y+1 } ;
	println!("The result is {}",x(10));
	{
			let k = 43;
			x = | y:i32 | -> i32 { z + k + y + 1 };
	}
abpicoli@DESKTOP-EPFPMPH:/mnt/e/projetos/my_rust_journal/ch13_skim/src$ cargo run
   Compiling ch13_skim v0.1.0 (/mnt/e/projetos/my_rust_journal/ch13_skim)
error[E0308]: mismatched types
 --> src/main.rs:8:8
  |
4 |     let mut x = |y:i32 | -> i32 { z+y+1 } ;
  |                 --------------- the expected closure
...
8 |             x = |y:i32 | -> i32 { z + k + y + 1 };
  |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure
  |

Closures are not exactly overridable by anything other than the original closure. So, can you use it as a discardable parameter within a function call? YES. Can you do some crazy s*t by changing functions in the middle of the code? No.

The book explains further that you can be "lazy" about closures. Their intended used is within a normal function call... discardable things...

Because of that you can omit the signature, because it is "obvious" what the parameters are when a return type is expected.

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

by default closures takes the least invasive approach to consume a parameter:

It borrows immutably if it can. It borrows mutably if the original variable is mutable.

If you really want a closure to move and consume the function, you need to add the move keyword.

// borrows immutable:
let only_borrows = || println!("From closure: {:?}", list);

// borrows mutable: (because of the mut keyword?)
let mut borrows_mutably = || list.push(7);

If you don't place the "mut" keyword in the borrows_mutable, the closure is not considered a mutable closure (that is, a closure that changes the inbound content).

error[E0596]: cannot borrow `borrows_mutably` as mutable, as it is not declared as mutable
 --> src/main.rs:8:2
  |
6 |     let borrows_mutably = || list.push(7);
  |                              ---- calling `borrows_mutably` requires mutable binding due to mutable borrow of `list`
7 |
8 |     borrows_mutably();
  |     ^^^^^^^^^^^^^^^ cannot borrow as mutable
  |
help: consider changing this to be mutable
  |
6 |     let mut borrows_mutably = || list.push(7);
  |         +++

Regarding the "move" thing:

to simulate stuff like this:

fn throwAway(x:String) {
	
		println!("This string will be consumed forever:{}",x);
	
}

fn main() {
    println!("Hello, world!");
	let mut list = vec![1, 2, 3];
    println!("Before defining closure: {:?}", list);
	// will this throw an error? What error?
    let borrows_mutably = || list.push(7);
	
	
	borrows_mutably();
	println!("After defining closure: {:?}", list);

	let something_that_is_moved=String::from("Oh oh");
	throwAway(something_that_is_moved);
	println!("It is lost, right? {}",&something_that_is_moved);
// error[E0382]: borrow of moved value: `something_that_is_moved`
  // --> src/main.rs:20:35
   // |
// 18 |     let something_that_is_moved=String::from("Oh oh");
   // |         ----------------------- move occurs because `something_that_is_moved` has type `String`, which does not implement the `Copy` trait
// 19 |     throwAway(something_that_is_moved);
   // |               ----------------------- value moved here
// 20 |     println!("It is lost, right? {}",&something_that_is_moved);
   // |                                      ^^^^^^^^^^^^^^^^^^^^^^^^ value borrowed here after move
   // |
// note: consider changing this parameter type in function `throwAway` to borrow instead if owning the value isn't necessary
	
}

Can we make the "throwaway" function as a closure?

MIND THAT Rust is smart(ish) but not that smart:

	let throw_away = |x| -> () { println!("Will not throw away, right? {}",x) };
	let something_that_is_moved=String::from("Oh oh");
	throw_away(something_that_is_moved); // THIS WILL THROW SOMETHING_THAT_IS_MOVED AWAY, ANYWAY! Rust identified that *"Oh, ok, x is of type String! Not &String, &mut String... String)."*

But ... MIND THE MESSAGE:

error[E0382]: borrow of moved value: something_that_is_moved --> src/main.rs:12:42 | 10 | let something_that_is_moved=String::from("Oh oh"); | ----------------------- move occurs because something_that_is_moved has type String, which does not implement the Copy trait 11 | throw_away(something_that_is_moved); | ----------------------- value moved here 12 | println!("This is not moved, right? {}",&something_that_is_moved); | ^^^^^^^^^^^^^^^^^^^^^^^^ value borrowed here after move | help: consider cloning the value if the performance cost is acceptable | 11 | throw_away(something_that_is_moved.clone()); | ++++++++

String does not implement the Copy trait. Will it work for a vec! ?

Nope.

The big difference between my examples and the book itself is that I'm passing an input parameter!!

ORIGINAL CHAPTER 13 EXAMPLE:

let only_borrows = ||  println!("From closure: {:?}", list);
                   ↑
				   |--- no parameter here

There is no miracle: if I pass a parameter to a closure, it will follow the same rules that a function would.

Nov/29/2023

Still studying closures.

Also skimmed through the crates system and rustdoc. (ch.14)

I have a worry about rust... memory allocation. Since there is no constrains and no garbage collection, and hence no memory manager, memory fragmentation does occur. Which means that, depending on the application, rust can become somewhat greedy and unchecked.

I've searched for "heap management" in rust.

I'm now skimming through the smart pointer systems.

Stopped in RC smart pointer.

Nov/30/2023

Keep reading chapter 15 - smart pointers https://rust-book.cs.brown.edu/ch15-04-rc.html

SESSION 1: 9:12brt - 9:43

Session 2: 9:49brt - 10:24 fearless concurrency https://rust-book.cs.brown.edu/ch16-00-concurrency.html

Session 3: 10:30 - 11:04 fearless concurrency https://rust-book.cs.brown.edu/ch16-02-message-passing.html

Session 4: 11:10 - 11:44 https://rust-book.cs.brown.edu/ch16-04-extensible-concurrency-sync-and-send.html

Dec/1st/2023

Session 1: 8:20brt-8:44 https://rust-book.cs.brown.edu/ch17-05-design-challenge.html#ch17-05-design-challenge-references-q Session 2: 8:52brt- 9:13 - having a bad day today.

Dec/3rd/2023

Creating a big post in the rust forums. And then decided not to post it.

Dec/4th/2023:

9:41 - 10:32 https://rust-book.cs.brown.edu/ch18-00-patterns.html starting to see unsafe rust.

10:37 - 11:12 https://rust-book.cs.brown.edu/ch19-06-macros.html

Well... that finishes the "book skimming".

Discussion on the framework project we have at work.

  1. Processors are configured with a configuration file.

  2. Processors specify the next operation.

  3. Processors will work with an agnostic hierarchical tree?

Dec/5th/2023:

After some soul searching, I've decided to fall back into the rust book, and try to answer all the quizzes. Got into chapter 4 so far.

I got a strange code snippet, that I want to test if it does make sense:

fn main() {
    let first = String::from("Ferris"); // THIS IS A NON-MUTABLE VARIABLE!!!
    let full = add_suffix(first); // AND I CAN USE IT INTO A MUTABLE FUNCTION!
    println!("{full}");
}

fn add_suffix(mut name: String) -> String {
    name.push_str(" Jr.");
    name
}

And... yes, it works!

I believe what I can't do is to invoke a mutable method from an immutable variable.

But I can move the unmutable variable into a mutable variable, perform the mutation, and then move it back.

fn main() {
	let first = String::from("Ferris");
	let mut first = first; // making first mutable.
	first.push_str(" Jr.");
	println!("{first}");
}

Now, I suppose I can't set the string is mutable from inside the function, without explicitly saying the parameter is mutable in the first place, right?

fn main() {
    let first = String::from("Ferris"); // THIS IS A NON-MUTABLE VARIABLE!!!
    let full = add_suffix(first); // AND I CAN USE IT INTO A MUTABLE FUNCTION!
    println!("{full}");
}

fn add_suffix(name: String) -> String {
    let mut name = name;
	name.push_str(" Jr.");
    name
}

AND... it COMPILES!!!

I believe that, while there is nobody else borrowing a reference to the variable, mutating it will work.

So, this won't work.

I'm still confused with chapter 4.1 and what do they consider undefined behavior. And discovered that the heap is shared by The whole operating system .

Java gives this impression that the heap is some safe construct implicit to a language itself. It isn't. The operating system is the ultimate memory provider, after all.

So, to have two free() operations is asking the operating system to free memory pointed by address X, twice. The first call will contain the data you have allocated yourself.

In between another process may have allocated memory in the same address. And a free will free the pointer from ANOTHER PROCESS. Dangerous stuff indeed: because then a third process may allocate the same address, write some garbage, and then give the second process a very cryptic content, that has nothing to do with what the program demanded.

Pseudocode for three programs that will run concurrently

NOTE: I'm aware that heap doesn't work in such small memory blocks. I'm just hipothesizing here a device with very low memory where OS memory requests are very fine-grained.

It all depends on the logic used for memory allocation x actual operating system allocations, that I'm treating as a black box here.

Program 1:

1."Allocate 100 bytes for me to write an SQL statement."

  1. "Write into the address the query "SELECT 1 FROM SYSDUMMY1"

  2. "Call the database with the query stored in the heap

Program 2:

  1. "Allocate 10 bytes for me to write BANANA 42"

  2. "Write 'BANANA 42' into the heap".

  3. "Send the message into the console".

  4. "Free the heap content"

  5. "Free the heap again"

Program 3:

  1. "Allocate 100 bytes for me."

  2. Read a message from sysin.

The operating system because of concurrency will run this programs in sequence for each one, but in arbitrary order between them.

So, imagine this scenario: Program 2.1 and 2.2 and 2.3 and 2.4 is executed. At the heap position, say, 100, I will have "BANANA 42" inside. But since I've freed the variable, position 100 is available again for data.

Program 1.1 and 1.2 runs, and overwrites the content at position 100 with "SELECT 1 FROM SYSDUMMY1". After all 2.4 did free the space, right?

Program 2.5 runs, freeing the space again.

Program 3.1 and 3.2 run. Because address 100 was "free", the content of 100 is what the user typed, say, "HELLO WORLD!" : so at 100: I will have HELLO WORLD!M SYSDUMMY1

Program 1.3 will try to query the database with the query "HELLO WORLD!M SYSDUMMY1" . BOOM!

https://rust-book.cs.brown.edu/ch04-01-what-is-ownership.html

Stopped at chapter 4-2 https://rust-book.cs.brown.edu/ch04-02-references-and-borrowing.html