Systems in Rust
/dev/random
, reflect this:Note
A byte is a hardware idea - an ordered collection of 8 bits, which themselves are hardware units either containing, or lacking, some electrical charge. We view them as storing zeros or ones.
+
or -
but that perform operations on pairs of bits rather than pairs of numeric values.std
in a few places, though there is no formal byte
type.u8
is used.
bytes
and the data structure Bytes
u8
values.as_bytes
, an array of u8
is often used (expressed as [u8]
).u8
s./dev/random
and taking a peek.b
format code, to see the individual zeroes and ones of a byte or bytes.:b
$ cargo run
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/scratch`
1000111011010011001001000110100010011010101101010111100001010101
$ cargo run
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/scratch`
1010101000100001011000111011111111011110010001010010101000111010
$ cargo run
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/scratch`
1001110000001001110011100111110010010101110000010111010111110011
u8
s, just for simplicity.let mut devrnd = std::fs::File::open("/dev/urandom").unwrap();
let mut buffer = [0u8; 2];
std::io::Read::read_exact(&mut devrnd, &mut buffer).unwrap();
let x = buffer[0];
let y = buffer[1];
println!("x = {x:08b}, y = {y:08b}");
:b
doesn’t render leading zeros, which is fine for numbers but not great for bytes.:08b
- where 0
specifies to have leading zeros, and 8
specifies how many digits.$ cargo run
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/scratch`
x = 00101010, y = 01100111
Operator | Example | Explanation |
---|---|---|
! |
!expr |
Bitwise or logical complement |
& |
expr & expr |
Bitwise AND |
<< |
expr << expr |
Left-shift |
>> |
expr >> expr |
Right-shift |
^ |
expr ^ expr |
Bitwise exclusive OR |
| |
expr | expr |
Bitwise OR |
bin
or :b
x = 00001111, y = 00110011
!=
# Print a truth table in markdown
print("| x | y | x!=y| x^y |")
print("|-----|-----|-----|-----|")
for left in (True, False):
for rite in (True, False):
print('|{:<5}|{:<5}|{:<5}|{:<5}|'.format(*[str(b) for b in [left, rite, left != rite, left ^ rite]]))
from operator import and_, or_ , xor
for op in [and_, or_ , xor]:
print('f({:08b}, {:08b}) = {:08b} for f ='.format(x, y, op(x,y)), op)
not
is similar but unary.!
for not (vs. ~
)src/main.rs
fn main() {
let mut devrnd = std::fs::File::open("/dev/urandom").unwrap();
let mut buffer = [0u8; 2];
std::io::Read::read_exact(&mut devrnd, &mut buffer).unwrap();
let x = buffer[0];
let y = buffer[1];
let mut z = x | y;
println!("x = {x:08b}, y = {y:08b}, x | y = {z:08b}");
z = x ^ y;
println!("x = {x:08b}, y = {y:08b}, x ^ y = {z:08b}");
z = x & y;
println!("x = {x:08b}, y = {y:08b}, x & y = {z:08b}");
let y = !buffer[0];
println!("x = {x:08b},!x = {y:08b}");
}
$ cargo run
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/scratch`
x = 01110000, y = 10001110, x | y = 11111110
x = 01110000, y = 10001110, x ^ y = 11111110
x = 01110000, y = 10001110, x & y = 00000000
x = 01110000,!x = 10001111
$ cargo run
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/scratch`
x = 10111111, y = 00110110, x | y = 10111111
x = 10111111, y = 00110110, x ^ y = 10001001
x = 10111111, y = 00110110, x & y = 00110110
x = 10111111,!x = 01000000
1
)0
)arr[0]
of something before the arr[1]
of something.0x1234
followers on Instagram📷stdin
and read_line
to read in some values.src/main.rs
cargo run
-> type some nonsense -> press ENTER$ cargo run
Compiling scratch v0.1.0 (/home/user/tmp/scratch)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.23s
Running `target/debug/scratch`
0x1234
0x
12
34
$ cargo run
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/scratch`
0x123
0x
12
3
next()
to an iterator to read characters..next()
, we get an option.
.next()
element.fn last(mut cs:std::str::Chars) -> Option<char> {
return match cs.next() {
Some(n) => match last(cs) { // if there's digits here, look for more
Some(m) => Some(m), // if the last is found, return it
None => Some(n), // no more letters - we are last, return n
},
None => None, // required by `rustc` in case we don't input anything
}
}
$ cargo run
Compiling scratch v0.1.0 (/home/user/tmp/scratch)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.22s
Running `target/debug/scratch`
0x1234
Some('\n')
Some('\n')
to None
fn last(mut cs:std::str::Chars) -> Option<char> {
return match cs.next() {
Some(n) => match last(cs) { // if there's digits here, look for more
Some(m) => {
println!("{n}{m}"); // If last is found, print this then last
Some(m), // if the last is found, return it
None => Some(n), // no more letters - we are last, return n
},
None => None, // required by `rustc` in case we don't input anything
}
}
$ cargo run
Compiling scratch v0.1.0 (/home/user/tmp/scratch)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.23s
Running `target/debug/scratch`
0x1234
34
24
14
Some('4')
main
None
after printing a pair.last
to pairs
main
if let
- kind of a single pattern match.src/main.rs
fn pairs(mut cs:std::str::Chars) -> Option<char> {
return match cs.next() {
Some('\n') => None,
Some(n) => match pairs(cs) {
Some(m) => {
println!("{n}{m}");
None
}
None => Some(n),
},
None => None,
}
}
fn main() {
let mut guess = String::new();
std::io::stdin().read_line(&mut guess).unwrap();
let mut cs = guess.chars();
cs.next();
cs.next();
if let Some(n) = pairs(cs) {
println!("{n}");
}
}
{n}{m}
from_str_radix
for e.g. u8
.
'1' => 1
instead)<class 'list'>
I can’t tell the difference.String
s with String::new()
Vec::new()
or the macro vec!
. Read morepairs
to a helper function.pairs
but add an argument.fn chars_to_vec(mut cs:std::str::Chars) -> Vec<u8> {
let mut vals = Vec::new();
// from old main
cs.next();
cs.next();
if let Some(n) = pairs(cs, &mut vals) {
vals.push(two_hex('0', n));
}
// "Prints and returns the value of a given
// expression for quick and dirty debugging."
dbg!(&vals);
return vals;
}
dbg!
“borrow” the vector to print it.fn pairs(mut cs:std::str::Chars, vals:&mut Vec<u8>) -> Option<char> {
return match cs.next() {
Some('\n') => None,
Some(n) => match pairs(cs, vals) {
Some(m) => {
vals.push(two_hex(n,m)); // only change
None
},
None => Some(n),
},
None => None,
}
}
u8
s into it.0..4
) as the addresses in computer memory.A big-endian system stores the most significant byte (MSB) of a word at the smallest memory address and the least significant byte (LSB) at the largest.
A little-endian system, in contrast, stores the least-significant byte (LSB) at the smallest address.
0x01020304
, the MSB is 1
, and LSB is 4
[4, 3, 2, 1]
, the smallest memory address contains a 4
.main
$ cargo r
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/scratch`
0x01020304
[src/main.rs:10:5] &vals = [
4,
3,
2,
1,
]
[src/main.rs:38:9] (i, vals[i]) = (
0,
4,
)
[src/main.rs:38:9] (i, vals[i]) = (
1,
3,
)
[src/main.rs:38:9] (i, vals[i]) = (
2,
2,
)
[src/main.rs:38:9] (i, vals[i]) = (
3,
1,
)
1020304
<<
(well, left shift)fn custom_u8s_to_u32(vals : Vec<u8>) -> u32 {
// We'll just take the first four for now
let mut ret : u32 = 0;
for i in 0..4 {
dbg!((i, vals[i]));
ret += (vals[i] as u32) << (8 * i);
}
return ret;
}
as u32
!