← Back to Course
# Binary and Bases in Rust ## CS 631 Systems Foundations --- ## Rust Integer Types Built-in, no imports needed: | Type | Width | Signed? | |------|-------|---------| | `u8` / `i8` | 8 bits | No / Yes | | `u16` / `i16` | 16 bits | No / Yes | | `u32` / `i32` | 32 bits | No / Yes | | `u64` / `i64` | 64 bits | No / Yes | ```rust let a: u32 = 42; let b: u8 = 255; // let c: u32 = a + b; // ERROR: type mismatch let c: u32 = a + b as u32; // OK: explicit cast ```
No implicit widening — must use
as
to cast.
--- ## The `as` Keyword ```rust let unsigned: u32 = 0xFFFFFFFB; let signed: i32 = unsigned as i32; // -5 (reinterpret bits) let back: u32 = signed as u32; // 0xFFFFFFFB (reinterpret) ``` | Cast | Effect | |------|--------| | `u32` → `i32` | Reinterpret bits (same width) | | `i32` → `u32` | Reinterpret bits (same width) | | `u8` → `u32` | Zero-extend | | `i8` → `i32` | Sign-extend | --- ## Bitwise NOT: `!` not `~`
**C:** ```c uint32_t a = 0x0F; uint32_t b = ~a; // b = 0xFFFFFFF0 ```
**Rust:** ```rust let a: u32 = 0x0F; let b = !a; // b = 0xFFFFFFF0 ```
`!` in Rust does both: - **Bitwise NOT** on integers - **Logical NOT** on booleans --- ## Bitwise AND, OR, XOR Same operators as C: `&`, `|`, `^` ```rust let a: u32 = 0xCA; // 1100 1010 let b: u32 = 0xF0; // 1111 0000 let and_ab = a & b; // 0xC0 let or_ab = a | b; // 0xFA let xor_ab = a ^ b; // 0x3A ```
Type safety: both operands must be the same type!
```rust let a: u32 = 0xFF; let b: u8 = 0x0F; let c = a & (b as u32); // Must cast ``` --- ## Shift Operators: Guaranteed Behavior | Type | `>>` | `<<` | |------|------|------| | `u32` | Logical (fill 0) | Fill 0 | | `i32` | Arithmetic (fill sign) | Fill 0 | ```rust let u: u32 = 0x80000000; u >> 4 // 0x08000000 (logical, always) let s: i32 = -16; // 0xFFFFFFF0 s >> 2 // -4 (arithmetic, always) ```
No implementation-defined behavior! Unlike C.
--- ## NTLang Shift Operators | NTLang | Rust Implementation | |--------|---------------------| | `<<` | `left << right` | | `>>` | `left >> right` (u32 = logical) | | `>-` | `((left as i32) >> right) as u32` | ```rust Operator::Lsl => left << right, Operator::Lsr => left >> right, Operator::Asr => ((left as i32) >> right) as u32, ``` Cast to `i32` for ASR, back to `u32` for storage. --- ## Overflow Protection ```rust let x: u32 = 1; // x << 32 → PANIC in debug mode! // Safe alternative: x.wrapping_shl(32) // Always 0, never panics ``` | Mode | `a + b` overflow | `a << 32` | |------|------------------|-----------| | Debug | Panic | Panic | | Release | Wraps | Wraps | | `wrapping_*` | Wraps (always) | Wraps (always) | --- ## String-to-Integer ```rust pub fn conv_str_to_u32(s: &str) -> Option
{ let (base, digits) = if s.starts_with("0b") { (2, &s[2..]) } else if s.starts_with("0x") { (16, &s[2..]) } else { (10, s) }; let mut value: u32 = 0; for c in digits.chars() { let digit = c.to_digit(base)?; value = value * base + digit; } Some(value) } ``` --- ## Key Rust Features **`to_digit(base)`** — handles all cases: ```rust 'A'.to_digit(16) // Some(10) '9'.to_digit(10) // Some(9) '2'.to_digit(2) // None (invalid) ``` **`?` operator** — propagates `None`: ```rust let digit = c.to_digit(base)?; // If None, function returns None immediately ```
Compare to C:
exit(-1)
vs Rust's
Option<T>
--- ## Integer-to-String ```rust pub fn conv_u32_to_decstr(value: u32) -> String { if value == 0 { return String::from("0"); } let mut digits = Vec::new(); let mut v = value; while v > 0 { digits.push((b'0' + (v % 10) as u8) as char); v /= 10; } digits.iter().rev().collect() } ``` Returns owned `String` (not a buffer pointer). --- ## Hex and Binary Conversion ```rust pub fn conv_u32_to_hexstr(value: u32) -> String { let hex = ['0','1','2','3','4','5','6','7', '8','9','A','B','C','D','E','F']; if value == 0 { return String::from("0x0"); } let mut digits = Vec::new(); let mut v = value; while v > 0 { digits.push(hex[(v % 16) as usize]); v /= 16; } let s: String = digits.iter().rev().collect(); format!("0x{}", s) } ``` --- ## Width Mask ```rust pub fn conv_width_mask(width: u32) -> u32 { if width >= 32 { return 0xFFFFFFFF; } 1u32.wrapping_shl(width) - 1 } ``` | Width | Mask | |-------|------| | 4 | `0x0F` | | 8 | `0xFF` | | 16 | `0xFFFF` | | 32 | `0xFFFFFFFF` | `wrapping_shl` avoids panic on over-shift. --- ## Base Dispatch with Pattern Matching ```rust pub fn conv_u32_to_str(value: u32, base: u32) -> String { match base { 2 => conv_u32_to_binstr(value), 10 => conv_u32_to_decstr(value), 16 => conv_u32_to_hexstr(value), _ => panic!("Unsupported base: {}", base), } } ``` --- ## Signed Output ```rust pub fn print_signed(value: u32, width: u32, base: u32) { let mask = conv_width_mask(width); let masked = value & mask; let msb = width - 1; if (masked >> msb) & 1 == 1 { // Negative: sign-extend, negate, print with '-' let extended = sign_extend(masked, width); let positive = (-(extended as i32)) as u32; let s = conv_u32_to_str(positive, base); print!("-{}", s); } else { print!("{}", conv_u32_to_str(masked, base)); } } ``` --- ## C vs Rust Comparison | Aspect | C | Rust | |--------|---|------| | NOT | `~` | `!` | | Fixed types | `
` | Built-in | | `>>` signed | Impl-defined | Arithmetic (guaranteed) | | Over-shift | Undefined | Panic/wrap | | Char→digit | `c - '0'` | `.to_digit(base)` | | Errors | `exit(-1)` | `Option`/`Result` | | Strings | `char*` buffer | `String` (owned) | | Cast | `(int32_t)x` | `x as i32` | --- ## Summary (1/2) 1. **Integer types** — `u32`/`i32` built-in, `as` for casting 2. **NOT is `!`** — not `~`, works on both ints and bools 3. **Shifts guaranteed** — `>>` on `i32` = ASR, on `u32` = LSR 4. **`wrapping_*`** — safe overflow behavior in all modes --- ## Summary (2/2) 5. **`to_digit(base)`** — clean char→digit conversion 6. **`Option
`** — replaces `exit(-1)` for error handling 7. **`String` ownership** — functions return owned strings 8. **Pattern matching** — clean dispatch for base selection