From 432b87afaaa204fc618732b3b2f2e8a1bbc2d343 Mon Sep 17 00:00:00 2001 From: anonfedora Date: Mon, 20 Jan 2025 11:24:11 +0100 Subject: [PATCH] feat: assignment 3 --- src/fp.md | 52 ++++++++++++++++ src/main.rs | 164 +++++++++++++++---------------------------------- src/pointer.md | 149 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 250 insertions(+), 115 deletions(-) create mode 100644 src/fp.md create mode 100644 src/pointer.md diff --git a/src/fp.md b/src/fp.md new file mode 100644 index 0000000..67023fd --- /dev/null +++ b/src/fp.md @@ -0,0 +1,52 @@ +# Understanding f32 and f64 in Rust + +## Overview +Rust provides two floating-point types: `f32` (32-bit) and `f64` (64-bit). Both follow the IEEE 754 standard for floating-point arithmetic. + +## Key Differences + +### Precision +- `f32`: Single precision, ~6-7 decimal digits of precision +- `f64`: Double precision, ~15-17 decimal digits of precision + +### Memory Usage +- `f32`: 4 bytes +- `f64`: 8 bytes + +### Range +- `f32`: ±3.4E+38 to ±3.4E-38 +- `f64`: ±1.8E+308 to ±2.2E-308 + +### Use Cases + +#### f32 is preferred when: +- Working with graphics and game development +- Memory is constrained +- Performance is critical +- Full double precision isn't necessary +- Working with hardware that natively uses 32-bit floats + +#### f64 is preferred when: +- Higher precision is required +- Working with scientific calculations +- Dealing with large numerical ranges +- Financial calculations (though decimals are often better) +- Default choice for general-purpose floating-point calculations + +### Performance Considerations +- `f32` operations are generally faster +- Modern CPUs might handle `f64` just as efficiently +- `f32` arrays use less cache space +- SIMD operations can process more `f32` values simultaneously + +### Common Pitfalls +1. Precision loss in `f32` during complex calculations +2. Comparison issues due to floating-point rounding +3. Accumulated errors in long calculation chains +4. Platform-dependent behavior + +## Best Practices +1. Use `f64` by default unless you have specific reasons not to +2. Avoid direct equality comparisons +3. Consider using decimal types for financial calculations +4. Document precision requirements in your code \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index c8c63c7..05fc032 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,129 +1,63 @@ -fn main() { - // intro_to_u(); - string_handler(); -} - -// function to encapsulate all integers -fn intro_to_u(){ - let sum_result: u8 = sum(5, 10); - let mult_result: u64 = multiply(5, 10); - let divide: f64 = divide(20.0, 10.2); - let subtr: isize = substract(20, 10); - let check: bool = check_func(5, 10); - println!("Sum: {}", sum_result); - println!("Multiplication: {}", mult_result); - println!("Division: {}", divide); - println!("Substraction: {}", subtr); - println!("Check: {}", check); - - let sum_result: f64 = sumfp(5.0, 10.0); - let mult_result: f64 = multiply_fp(5.0, 10.0); - let divide: f64 = divide_fp(20.0, 10.2); - let subtr: f64 = substract_fp(20.0, 10.0); - println!("Sum: {}", sum_result); - println!("Multiplication: {}", mult_result); - println!("Division: {}", divide); - println!("Substraction: {}", subtr); - - let full_name = string_formatting(convert_to_string_v1("Akinshola"), convert_to_string_v2("Akinniyi")); - println!("Full Name: {}", full_name); -} - -fn sum(x: u8, y: u8) -> u8 { - x + y -} -fn multiply(x: u64, y: u64) -> u64 { - x * y -} -fn divide(x: f64, y: f64) -> f64 { - let res: f64 = x / y; - return res -} -fn substract(x: isize, y: isize) -> isize { - x - y -} - -fn sumfp(x: f64, y: f64) -> f64 { - x + y -} -fn multiply_fp(x: f64, y: f64) -> f64 { - x * y -} +// Example usage +fn main() { + // Integer conversion examples + let small_num: u8 = 255; + let big_num = u8_to_u32(small_num); + println!("Converted u8 to u32: {}", big_num); + + let large_num: u32 = 256; + match u32_to_u8(large_num) { + Some(num) => println!("Converted u32 to u8: {}", num), + None => println!("Value {} is too large for u8", large_num), + } -fn divide_fp(x: f64, y: f64) -> f64 { - x / y + // String conversion example + let owned_string = String::from("Hello, World!"); + let str_ref = string_as_str(&owned_string); + println!("String as str: {}", str_ref); + + // Arithmetic example + let a = 100; + let b = 20; + if let Some((sum, diff, prod, quot)) = checked_arithmetic(a, b) { + println!( + "Sum: {}, Difference: {}, Product: {}, Quotient: {}", + sum, diff, prod, quot + ); + } } -fn substract_fp(x: f64, y: f64) -> f64 { - x - y +// Convert from low integer to high integer (u8 -> u32) +fn u8_to_u32(value: u8) -> u32 { + value as u32 } -fn check_func(num1: u8, num2: u8) -> bool { - let sum_of_two_nums = sum(num1, num2); - if sum_of_two_nums % 2 == 0 { - println!("The sum of {} and {} is even", num1, num2); - return true; +// Convert from high bit to low bit with overflow checking +fn u32_to_u8(value: u32) -> Option { + if value <= u8::MAX as u32 { + Some(value as u8) } else { - println!("The sum of {} and {} is odd", num1, num2); - return false; + None } } -// fn string_formatting(first_name: &str, last_name: &str) -> String { -// let full_name = format!("{} {}", first_name, last_name); -// return full_name; -// } -fn string_formatting(first_name: String, last_name: String) -> String { - let full_name = format!("{} {}", first_name, last_name); - return full_name; -} - -// util fn version 1 to convert &str to String -fn convert_to_string_v1(x: &str) -> String { - x.to_string() -} - -// util fn version 2 to convert &str to String -fn convert_to_string_v2(x: &str) -> String { - String::from(x) -} - -======= -// function that encapsulate all integers -fn intro_to_u() { - // subtract - // multiplication - // division - let sum_result: u8 = sum(5, 10); - println!("the sum result is: {}", sum_result); -} - -fn sum(x: u8, y: u8) -> u8 { - x + y // implicit return - // return x + y; // explicit return -} - -// handle all string-related functions -fn string_handler() { - // intro_to_str_slice(); - intro_to_ownable_string(); +// Convert String to &str (Safe string to str conversion that doesn't leak memory) +fn string_as_str(s: &String) -> &str { + s.as_str() } -// intro string slice -// for fixed-sized strings -fn intro_to_str_slice() { - let name: &str = "Sylvia"; - println!("my name is {}", name) -} +// Arithmetic operations on signed integers with overflow checking +fn checked_arithmetic(a: i32, b: i32) -> Option<(i32, i32, i32, i32)> { + // Returns (sum, difference, product, quotient) + let sum = a.checked_add(b)?; + let difference = a.checked_sub(b)?; + let product = a.checked_mul(b)?; + let quotient = if b != 0 { + a.checked_div(b)? + } else { + return None; + }; -fn intro_to_ownable_string() { - let mut name: String = String::from("Wisdom"); - println!("first name: here: {}", name); - name.push_str(" John"); - println!("final name: here: {}", name); - println!("ptr = address in heap memory: {:?}", name.as_ptr()); + Some((sum, difference, product, quotient)) } - - - diff --git a/src/pointer.md b/src/pointer.md new file mode 100644 index 0000000..61ec582 --- /dev/null +++ b/src/pointer.md @@ -0,0 +1,149 @@ +# Pointers in Rust Memory Management + +## Overview +Rust's pointer system is fundamental to its memory safety guarantees. Unlike C/C++, Rust provides several pointer types with different ownership and borrowing semantics. + +## Types of Pointers + +### References (&T and &mut T) +- Most basic form of Rust pointers +- Non-nullable +- Always valid +- Follow borrowing rules +- Zero runtime cost +- Lifetime checked at compile time + +Example: +```rust +fn process(data: &u32) { + println!("Value: {}", *data); +} +``` + +### Box +- Heap allocation +- Single owner +- Fixed size known at compile time +- Automatically deallocated when Box goes out of scope + +Example: +```rust +let boxed = Box::new(5); +``` + +### Raw Pointers (*const T and *mut T) +- Similar to C pointers +- No safety guarantees +- Used in unsafe code +- No automatic cleanup +- Can be null + +Example: +```rust +let mut value = 10; +let raw = &mut value as *mut i32; +``` + +## Memory Management Features + +### Ownership Rules +1. Each value has exactly one owner +2. When owner goes out of scope, value is dropped +3. Ownership can be transferred (moved) + +### Borrowing Rules +1. One mutable reference OR any number of immutable references +2. References must not outlive their referent +3. No null references possible + +### Smart Pointers +- Rc: Reference counted, multiple owners +- Arc: Atomic reference counting for thread-safety +- Weak: Weak references that don't prevent cleanup + +## Common Use Cases + +### Stack vs Heap Allocation +```rust +// Stack allocation +let x = 5; +let y = &x; // Reference to stack data + +// Heap allocation +let boxed = Box::new(5); +let reference = &*boxed; // Reference to heap data +``` + +### Dynamic Dispatch +```rust +trait Animal { + fn make_sound(&self); +} + +// Box is a pointer to any type implementing Animal +fn process_animal(animal: Box) { + animal.make_sound(); +} +``` + +### Recursive Data Structures +```rust +struct Node { + value: T, + next: Option>> +} +``` + +## Best Practices + +1. Prefer references over raw pointers +2. Use Box for heap allocation +3. Consider Rc/Arc for shared ownership +4. Document unsafe pointer usage +5. Implement Drop trait for custom cleanup +6. Use smart pointers appropriately +7. Understand lifetimes + +## Safety Considerations + +1. Memory Leaks Prevention +- Rust's ownership system prevents most memory leaks +- Resource cleanup is deterministic +- Drop trait ensures proper cleanup + +2. Thread Safety +- Send and Sync traits +- Arc for thread-safe reference counting +- Mutex and RwLock for synchronization + +3. Null Safety +- Option instead of null +- Never dereferencing null pointers +- Compile-time checks + +## Common Patterns + +### Interior Mutability +```rust +use std::cell::RefCell; + +let data = RefCell::new(5); +*data.borrow_mut() += 1; +``` + +### Shared Ownership +```rust +use std::rc::Rc; + +let shared = Rc::new(String::from("shared data")); +let clone = Rc::clone(&shared); +``` + +### Safe Raw Pointer Usage +```rust +unsafe fn dangerous_operation(ptr: *mut i32) { + if !ptr.is_null() { + *ptr += 1; + } +} +``` \ No newline at end of file