当前位置:网站首页>[rust notes] 18 macro

[rust notes] 18 macro

2022-07-06 03:19:00 phial03

18 - macro

  • assert_eq! macro : An error message containing the assertion file name and line number can be generated .

  • macro : Is a way to extend the language . During compilation , Before checking the type and generating any machine code , Every macro call will be extended (expanded).

    assert_eq!(gcd(6, 10), 2);
    
    //  The code after the above macro expansion 
    match (&gcd(6, 10), &2) {
          
        (left_val, right_val) => {
          
            if !(*left_val == *right_val) {
          
                panic!("assertion failed: '(left == right)', \ (left: '{:?}', rigth: '{:?}')", left_val, right_val);
            }
        }
    }
    
  • Rust Macros can be integrated with other components of the language , Not easy to make mistakes .

    • Macro calls are always marked with an exclamation point , Not easy to be ignored .
    • Rust Macros will never insert mismatched square brackets or parentheses .
    • Rust Macro built-in pattern matching , Easy to maintain and extend .

18.1 - Macro Foundation

  • macro_rules! yes Rust The main way of defining macro .

    marcro_rule! assert_eq {
          
        ($left: expr, $right: expr) => (  //  Pattern 
            {
            //  Templates 
            match (&$left, &$right) {
          
                if !(*left_val == *right_val) {
          
                    panic!("assertion failed: '(left == right)' \ (left: '{:?}', right: '{:?}')", left_val, right_val)
                }
                }
            }
        );
    }
    
    • Note that in the above code assert_eq There is no exclamation point behind .
    • Exclamation marks are required only when calling macros !, When defining, you don't need .
  • file!line! and macro_rules!, Itself is built into the compiler .

  • Another way of macro definition : Procedure macro (procedural macro).

  • macro_rule! The macro defined is completely based on the pattern matching implementation logic .

    (  Pattern 1 ) => (  Templates 1 );
    (  Pattern 2 ) => (  Templates 2 );
    ...
    
    • You can use parentheses around patterns and templates , Use square brackets or curly braces .

    • The following forms are equivalent :

      assert_eq!(gcd(6, 10), 2);
      assert_eq![gcd(6, 10), 2];
      assert_eq!{
              gcd(6, 10), 2}   //  When using curly braces , The last semicolon is optional .
      
  • Usage conventions or conventions of brackets :

    • Calling assert_eq! when , Use parentheses
    • Calling vec! Use square brackets
    • Calling macro_rules! Use curly braces

18.1.1 - Macro Extension Foundation

  • You cannot call a macro before defining it ,Rust Macro calls are analyzed and extended .

  • Macro mode operates on tokens (token), Such as digital 、 name 、 Punctuation, etc Rust Grammatical symbols of programs . Notes and whitespace are not notations .

  • For

    $left:expr
    

    The meaning of :

    • expr Is an expression , Its value will be assigned to $left.
    • Macro patterns are the same as regular expressions , Only a few special characters trigger special matching behavior .
    • Other characters , Like a comma , Need to match literally , Otherwise, matching will fail .

18.1.3 - repeat

  • The standard vec! There are two forms of macros :

    //  Repeat a value N Time 
    let buffer = vec![0_u8; 1000];
    
    //  A comma separated list of values 
    let numbers = vec!["udon", "ramen", "soba"];
    
  • vec! Implementation of macro :

    macro_rules! vec {
          
      ($elem: expr; $n: expr) => {
          
        ::std::vec::from_elem($elem, $n)
      };
      ( $( $x:expr ),* ) => {
          
        <[_]>::into_vec(Box::new([ $( $x ),* ]))
      };
      ( $( $x:expr ),+ ,) => {
          
        vec![ $( $x ),* ]
      };
    }
    
  • Repetitive patterns :

    Pattern meaning
    $( ... )* matching 0 Or many times , There is no separator
    $( ... ),* matching 0 Or many times , Separated by commas
    $( ... );* matching 0 Or many times , Semicolon separated
    $( ... )+ matching 1 Or many times , There is no separator
    $( ... ),+ matching 1 Or many times , Separated by commas
    $( ... );+ matching 1 Or many times , Semicolon separated
    • $x Is a set of expressions ;
    • <[_]> A slice that represents a certain type of value .
    • , Indicates matching a list with extra commas .

18.2 - Built in macro

  • file!(): Expand to a string literal , The current file name .

  • line!(): Expand to a u32 Literal , Represents the current line ( from 1 Start )

  • column!(): Expand to a u32 Literal , Represents the current column ( from 0 Start )

  • stringify!(...tokens...): Expand to a string literal , Contains the given token .

    • assert! Use this built-in macro to generate an error message containing assertion code .
    • If the parameter contains a macro , Then there will be no expansion , Such as stringify(line!()), Any parameter is a string "line!()".
  • concat!(str0, str1, ...): Expand to a string literal , Is the result of splicing its parameters .

  • cfg!(...): Expand to a Boolean constant , If the current build configuration matches the conditions in parentheses , Then for true.

  • env!("VAR_NAME"): Expand to a string , That is, specify the value of the environment variable at compile time . If the specified variable does not exist , A compilation error occurs . Often with Cargo Use a combination of .

  • option_env!("VAR_NAME"): And env! identical , But back to Option<&'static str>, If the specified variable is not set , Then return to None.

  • include!("file.rs"): Expand to the contents of the specified file , Must be effective Rust Code , For example, the sequence of expressions or characteristic items .

  • include_str!("file.txt"): Expand to a &'static str, Contains the text of the specified file .

    • Usage method :

      const COMPOSITOR_SHADER: &str =
          include_str!("../resources/compositor.glsl");
      
    • If the specified file does not exist , Or the text is not valid UTF-8, Causes a compilation error .

  • include_bytes!("file.dat"): Extend the file as binary data . As a result, &'static [u8] Type value .

  • Rules for built-in macros :

    • Processed at compile time , If the file does not exist or cannot be read , Then the compilation will fail .
    • All macros cannot fail at run time .
    • In any case , If the file name is a relative path , It will be parsed relative to the directory containing the current file .

18.3 - Debug macro

  • The process of macro expansion is invisible .

    • Rust In the process of extending macros , When some errors are found, an error message will be printed .
    • But the fully extended code with errors will not be displayed .
  • rustc
    

    It can show the information of the code after extending all macros .

    • cargo build --verbose You can see Cargo How to call rustc.
    • That is, copy rustc command , Add again -Z unstable-options --pretty expanded Options .
  • log_syntax!()
    

    macro : At compile time , Its parameters can be printed to the terminal .

    • Can be used to implement similar println! Debugging of .
    • It is required to have #![feature(log_syntax)] Characteristic marks .
  • Give Way Rust The compiler prints the logs of all macro calls to the terminal .

    • Insert... Somewhere in the code trace_macros!(true);.
    • such ,Rust Each macro expanded , Will print the name and parameters of the macro .

18.4 - Customize a macro ——json! macro

To develop a json! macro , Receive one JSON Value as parameter , Then expand it to something like the following Rust expression :

let students = json!([
  {
    
    "name": "Jim Blandy"
    "class_of": 1926
    "major": "Tibetan throat singing"
  },
]);

18.4.1 - Fragment type

  • macro_rules! Fragment types supported by macros :

    Fragment type matching ( Example ) You can add …
    expr expression :2 + 2, "udon", x.len()=> , ;
    stmt Expression or declaration , Do not include the semicolon at the end ( priority of use expr or block=> , ;
    ty type :StringVec<u8>(&str, bool)=> , ; =
    path route :ferns::std::sync::mpsc=> , ; =
    pat Pattern :_Some(ref x)=> , =
    item Characteristic item :struct Point {x: f64, y: f64}mod ferns; There is no limit
    block Code block :s += "ok\n"; true There is no limit
    meta Attribute body :inlinederive(Copy, Clone)doc="3D models." There is no limit
    ident identifier :stdJsonlongish_variable_name There is no limit
    tt Token tree :;>={}[0 1 (+ 0 1)] There is no limit
  • json! The definition of macro is as follows :

    macro_rules! json {
          
      (null) => {
          
        Json::Null
      };
      ([ $( $element:tt ),* ]) => {
          
        Json::Array(...)
      };
      ({
           $( $key:tt : $value:tt ),* }) => {
          
        Json::Object(...)
      };
      ($other:tt) => {
          
        ... // TODO:  return Number、String or Boolean
      };
    }
    

18.6 - transcend macro_rules!

  • Procedure macro :
    • Support extended #[derive] attribute , To handle custom features .
    • As Rust Function implementation , Not a declarative rule set .

See 《Rust Programming 》( Jim - Brandy 、 Jason, - By orendov , Translated by lisongfeng ) Chapter 20
Original address

原网站

版权声明
本文为[phial03]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/187/202207060317123588.html