Skip to content

Latest commit

 

History

History
112 lines (84 loc) · 3.43 KB

13.md

File metadata and controls

112 lines (84 loc) · 3.43 KB

Macros and Unsafe Code

macros are a way of writing a code that writes other codes. also know as meta programming.

example:

macro_rules! gcd {
    ($a: expr, $b: expr) => {{
        let mut m = $a;
        let mut n = $b;

        while m != 0 {
            if m < n {
                let t = m;
                m = n;
                n = t;
            }
            m = m % n;
        }
        n
    }};
}

procedural macros

used to generate code at compile time by taking aa piece of code as input and returning a transformed output

theres 3 types of macros:

  • function like macro: used to defined functions that takes in a set of args and generate a block of code hat is included in the program
  • attribute: used to add metadata to items in rust like structs, enums.
  • derive macros: automatically implement traits on structs or enums

TokenStreams: are types that represents a sequence of tokens, used to represent the output of a procedural macro. Quote: is a macro that can generate a token stream from a syntax tree. allows you to construct rust code using rust syntax syn create: third party rust parser that passes a rust code into a syntax that can be manipulated by a procedural macro

extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, ItemFn};

# [proc_macro_attribute]
pub fn debug_print(_attr: TokenStream, item: TokenStream) -> TokenStream {
    let mut item_fn = parse_macro_input!(item as ItemFn);
    let ident = &item_fn.sig.ident;
    item_fn.block.stmts.insert(
        0,
        syn::parse_quote!(println!("entering the function: {}", stringify!(#ident));
        ),
    );
    TokenStream::from(quote! {
        #item_fn
    })
}

Declarative Macros

declarative macros are used to match a given pattern and replace it with the specified code.

they can be used to define new controls structures or simplify repetitive code.

use macros::debug_print;

macro_rules! average {
    ($(,)*) => {
        0.0
    };

    ($($val:expr), + $(,)*) => {{
        let count = 0usize $(+ {let _ = stringify!($val); 1})*;
        let sum =  0.0 $(+ $val as f64)*;

        sum / count as f64
    }};
}

diff btw declarative macro and function like macro

declarative macro uses a set of rules to match and transform input tokens to output tokens. similar to regex that matches patterns and replaces with new patterns. they are defined using macro_rules and are evaluated compile time.

function like macro are also evaluated at compile time. operates like a regular function and accepts input arguments and returns output tokens. they can do more complex transformations and more flexible.

Unsafe Code

unsafe code allows us to bypass rust memory safety check in our code that are enforced during compile time.

let mut num = 5;
    let r1 = &num as *const i32;
    let r2 = &mut num as *mut i32;

    unsafe {
        println!("r1 is {:?}", *r1);
        println!("r2 is {:?}", *r2);
    }
  • when should i use unsafe code: situations where the safe abstractions provided by the language are not sufficient for the task at hand. example when interfacing with low level system api, hardware level tasks or implementing performant algorithms.

  • interfacing with c libraries

  • doing low level programming (where you need direct access to memory)

  • performance critical algorithm

  • custom data structure.

  • risks: can lead to undefined behaviors and memory safe issues.