From 3b0f45255b3b72a2ce4becfb50b598821ed65cf4 Mon Sep 17 00:00:00 2001 From: Brian Obot Date: Sun, 22 Mar 2026 17:14:26 +0100 Subject: [PATCH 1/2] Add Notes on Macro --- src/bin/45_strings_and_text.rs | 2 - src/bin/49_macros.rs | 146 +++++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 src/bin/49_macros.rs diff --git a/src/bin/45_strings_and_text.rs b/src/bin/45_strings_and_text.rs index 0af9c16..7503511 100644 --- a/src/bin/45_strings_and_text.rs +++ b/src/bin/45_strings_and_text.rs @@ -1,4 +1,3 @@ - fn main() { // Rust treats strings as UTF-8 Encoded by default let s = "你好 Rust"; @@ -31,4 +30,3 @@ fn main() { // .is_ascii_alphabetic() ... // } - diff --git a/src/bin/49_macros.rs b/src/bin/49_macros.rs new file mode 100644 index 0000000..fe1d2be --- /dev/null +++ b/src/bin/49_macros.rs @@ -0,0 +1,146 @@ +fn main() { + // an example of a macro is the assert_eq! macro + // this functionality could have been written as a generic function + // but macros do more than functions can do, for example, when an assert_eq! macro fails + // it prints the filename and the line number where the failure happened, fucntions have no way of getting that information + + // macros are expanded during the compilation phase before types are checked and well before + // any machine code is generated, they are expanded recursively into rust code, since macros expansion + // can themselves contain macros + // + // macros are differentiated from functions with the bang symbol before their parenthesis + // + // macro_rules! macro is the main way to define macros in Rust + // when defining macros, the ! is not used, it is only used when calling them + // a macro defined by macro_rules works entirely by pattern matching + /* + * (pattern 1) => (template 1) + * (pattern 2) => (template 2) + * + */ + + // you can use square brackets or curly braces for macros, they are still work + println!("Hello"); + println!["Hello"]; // this is why vec! is possible + println! {"Hello"}; + + // by convention, () are used for assert_*! macros, [] for vec! and {} for macro_rules! + // assert_eq! expands to + // assert_eq![2, 1 + 1]; + + /* + * match (&2, &(1 + 1)) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( + kind, + &*left_val, + &*right_val, + ::core::option::Option::None, + ); + } + } + }; + */ + + macro_rules! answer { + () => { + 42 + }; + } + + macro_rules! double { + ($x:expr) => { + $x * 2 + }; + } + + let result = double!(answer!()); + println!("Result: {}", result); + + // Some interestig macros + // file!() return a string path of the current file + // line!() return the line which the first macro that call it was called from + // column!() same as above, but for column + // include!("filename.rs"), includes the content of the file in the current line, the content must be valid Rust code + // include_str!("file.txt"), include the text content of the specified file + // include_bytes!("file.dat") same as above, but the file is treated a binary data + // todo!(), unimplemented!() + // matches!(value, pattern) + + // Building the json! Macro + use std::collections::HashMap; + + #[allow(dead_code)] + #[derive(Clone, PartialEq, Debug)] + enum Json { + Null, + Boolean(bool), + Number(f64), + String(String), + Array(Vec), + #[allow(clippy::box_collection)] + Object(Box>), + } + + macro_rules! json { + // the null literal must be matched directly + (null) => { Json::Null }; + ([ $( $element:tt ),* ]) => { Json::Array(vec![ $( json!($element) ),* ]) }; + ({ $( $key:tt : $value:tt ),* }) => { Json::Object(Box::new(vec![ + $( ($key.to_string(), json!($value)) ),* + ])) }; + ( $other:tt ) => { + Json::from($other) + } + } + + impl From for Json { + fn from(b: bool) -> Json { + Json::Boolean(b) + } + } + + impl From for Json { + fn from(value: String) -> Json { + Json::String(value) + } + } + + impl<'a> From<&'a str> for Json { + fn from(value: &'a str) -> Json { + Json::String(value.to_string()) + } + } + + macro_rules! impl_from_num_for_json { + ( $( $t:ident ),* ) => { + $( + impl From<$t> for Json { + fn from(n: $t) -> Json { + Json::Number(n as f64) + } + } + )* + }; + } + + impl_from_num_for_json!( + u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize, f32, f64 + ); + + assert_eq!(json!(null), Json::Null); + + let macro_generated_array = json!([1, 2, 3, 4]); + let hand_coded_array = Json::Array(vec![ + Json::Number(1.), + Json::Number(2.), + Json::Number(3.), + Json::Number(4.), + ]); + + assert_eq!(macro_generated_array, hand_coded_array); + + // Macros marked with #[macro_export] are automatically public to other modules +} From 9577455101f20037bc631e8641fd2b27c398862f Mon Sep 17 00:00:00 2001 From: Brian Obot Date: Tue, 31 Mar 2026 09:50:44 +0100 Subject: [PATCH 2/2] Add Some notes while reviewing strings and integer types --- src/bin/02ca_strings.rs | 5 +++++ src/bin/50_unsafe_code.rs | 8 ++++++++ 2 files changed, 13 insertions(+) create mode 100644 src/bin/50_unsafe_code.rs diff --git a/src/bin/02ca_strings.rs b/src/bin/02ca_strings.rs index 2718120..64f8f43 100644 --- a/src/bin/02ca_strings.rs +++ b/src/bin/02ca_strings.rs @@ -1,5 +1,10 @@ +// Some Notes: +// clones are expensive, since the data in the heap for the strings must be copied into another heap location +// borrowed strings &str are fixed sized references to the underlying str data + fn main() { let speech = "\"Ouch!\" said the well.\n"; + // All Strings are the same size because they are simply the size of the Fat pointer pointing to some region in the heap memory println!("Speech: {speech}"); diff --git a/src/bin/50_unsafe_code.rs b/src/bin/50_unsafe_code.rs new file mode 100644 index 0000000..9803c99 --- /dev/null +++ b/src/bin/50_unsafe_code.rs @@ -0,0 +1,8 @@ +fn print(message: &str) { + println!("{}", message); +} + +fn main() { + let message = "Hello world!"; + print(message); +}