Cách sử dụng chúng có thể cải thiện mã của bạn
Tạo mã là một tính năng bạn sẽ tìm thấy trong hầu hết các ngôn ngữ lập trình hiện đại. Nó có thể giúp bạn giảm mã soạn sẵn và sao chép mã, xác định ngôn ngữ dành riêng cho miền (DSL) và triển khai cú pháp mới.
Rust cung cấp một hệ thống macro mạnh mẽ cho phép bạn tạo mã tại thời điểm biên dịch để lập trình phức tạp hơn.
Mục Lục
Giới thiệu về Macro Rust
Macro là một loại siêu lập trình mà bạn có thể tận dụng để viết mã viết mã. Trong Rust, macro là một đoạn mã tạo mã khác tại thời điểm biên dịch.
Macro rỉ sét là một tính năng mạnh mẽ cho phép bạn viết mã tạo mã khác tại thời điểm biên dịch để tự động hóa các tác vụ lặp đi lặp lại. Các macro của Rust giúp giảm trùng lặp mã và tăng khả năng bảo trì và dễ đọc của mã.
Bạn có thể sử dụng Macro để tạo mọi thứ từ các đoạn mã đơn giản đến các thư viện và khung. Macro khác với các hàm Rust vì chúng hoạt động dựa trên mã chứ không phải dữ liệu khi chạy.
Xác định Macro trong Rust
Bạn sẽ xác định macro bằng macro_rules! vĩ mô. Các macro_rules! macro lấy một mẫu và một mẫu làm đầu vào. Rust khớp mẫu với mã đầu vào và sử dụng mẫu để tạo mã đầu ra.
Đây là cách bạn có thể xác định Macro trong Rust:
macro_rules! say_hello {
() => {
println!("Hello, world!");
};
}fn main() {
say_hello!();
}
Mã định nghĩa một nói_xin chào macro tạo mã để in “Xin chào, thế giới!”. Mã phù hợp với () cú pháp chống lại một đầu vào trống và inln! macro tạo mã đầu ra.
Đây là kết quả của việc chạy macro trong chủ yếu chức năng:
Macro có thể lấy đối số đầu vào cho mã được tạo. Đây là Macro nhận một đối số duy nhất và tạo mã để in thông báo:
macro_rules! say_message {
($message:expr) => {
println!("{}", $message);
};
}
Các say_message vĩ mô lấy tin nhắn $ đối số và tạo mã để in đối số bằng cách sử dụng inln! vĩ mô. Các expr cú pháp khớp với đối số với bất kỳ biểu thức Rust nào.
Các loại Macro Rust
Rust cung cấp ba loại macro. Mỗi loại macro phục vụ các mục đích cụ thể và chúng có cú pháp cũng như giới hạn.
Macro thủ tục
Macro thủ tục được coi là loại mạnh mẽ và linh hoạt nhất. Macro thủ tục cho phép bạn xác định cú pháp tùy chỉnh để tạo mã Rust đồng thời. Bạn có thể sử dụng macro theo thủ tục để tạo macro dẫn xuất tùy chỉnh, macro giống như thuộc tính tùy chỉnh và macro giống như chức năng tùy chỉnh.
Bạn sẽ sử dụng các macro dẫn xuất tùy chỉnh để tự động triển khai các cấu trúc và đặc điểm enum. Các gói phổ biến như Serde sử dụng macro dẫn xuất tùy chỉnh để tạo mã tuần tự hóa và giải tuần tự hóa cho cấu trúc dữ liệu Rust.
Các macro giống như thuộc tính tùy chỉnh rất hữu ích để thêm chú thích tùy chỉnh vào mã Rust. Khung web Rocket sử dụng macro giống như thuộc tính tùy chỉnh để xác định các tuyến đường một cách chính xác và dễ đọc.
Bạn có thể sử dụng các macro giống hàm tùy chỉnh để xác định các biểu thức hoặc câu lệnh Rust mới. Thùng Lazy_static sử dụng macro giống như chức năng tùy chỉnh để xác định lười khởi tạo biến tĩnh.
Đây là cách bạn có thể xác định macro thủ tục xác định macro dẫn xuất tùy chỉnh:
use proc_macro::TokenStream;
use quote::quote;
use syn::{DeriveInput, parse_macro_input};
Các sử dụng lệnh nhập các thùng và loại cần thiết để viết macro thủ tục Rust.
#[proc_macro_derive(MyTrait)]
pub fn my_derive_macro(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let name = &ast.ident; let gen = quote! {
impl MyTrait for #name {
}
};
gen.into()
}
Chương trình xác định một macro thủ tục tạo ra việc triển khai một đặc điểm cho một cấu trúc hoặc enum. Chương trình gọi macro có tên Mytrait trong thuộc tính dẫn xuất của cấu trúc hoặc enum. Macro mất một TokenStream đối tượng làm đầu vào chứa mã được phân tích cú pháp thành Cây cú pháp trừu tượng (AST) với parse_macro_input! vĩ mô.
Các tên biến là định danh cấu trúc hoặc enum dẫn xuất, biến trích dẫn! Macro tạo ra một AST mới đại diện cho việc triển khai Mytrait cho loại cuối cùng được trả lại dưới dạng TokenStream với vào trong phương pháp.
Để sử dụng macro, bạn cần nhập macro từ mô-đun mà bạn đã khai báo:
use my_macro_module::my_derive_macro;
Khi khai báo cấu trúc hoặc enum sử dụng macro, bạn sẽ thêm #[derive(MyTrait)] thuộc tính vào đầu khai báo.
#[derive(MyTrait)]
struct MyStruct {
}
Khai báo cấu trúc với thuộc tính mở rộng thành triển khai của Mytrait đặc điểm cho cấu trúc:
impl MyTrait for MyStruct {
}
Việc triển khai cho phép bạn sử dụng các phương thức trong Mytrait đặc điểm trên cấu trúc của tôi trường hợp.
Macro thuộc tính
Macro thuộc tính là các macro mà bạn có thể áp dụng cho các mục Rust như cấu trúc, enum, hàm và mô-đun. Macro thuộc tính có dạng một thuộc tính theo sau là một danh sách các đối số. Macro phân tích cú pháp đối số để tạo mã Rust.
Bạn sẽ sử dụng macro thuộc tính để thêm các hành vi và chú thích tùy chỉnh vào mã của mình.
Đây là macro thuộc tính thêm thuộc tính tùy chỉnh vào cấu trúc Rust:
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput, AttributeArgs};#[proc_macro_attribute]
pub fn my_attribute_macro(attr: TokenStream, item: TokenStream) -> TokenStream {
let args = parse_macro_input!(attr as AttributeArgs);
let input = parse_macro_input!(item as DeriveInput);
let name = &input.ident;
let gen = quote! {
#input
impl #name {
}
};
gen.into()
}
Macro lấy danh sách đối số và định nghĩa cấu trúc, đồng thời tạo cấu trúc đã sửa đổi với hành vi tùy chỉnh đã xác định.
Macro nhận hai đối số làm đầu vào: thuộc tính được áp dụng cho macro (được phân tích cú pháp bằng parse_macro_input! macro) và mục (được phân tích cú pháp bằng parse_macro_input! vĩ mô). Macro sử dụng trích dẫn! macro để tạo mã, bao gồm mục đầu vào ban đầu và mục bổ sung ngụ ý khối xác định hành vi tùy chỉnh.
Cuối cùng, hàm trả về mã được tạo dưới dạng TokenStream với vào trong() phương pháp.
Quy tắc vĩ mô
Quy tắc macro là loại macro đơn giản và linh hoạt nhất. Quy tắc macro cho phép bạn xác định cú pháp tùy chỉnh mở rộng thành mã Rust tại thời điểm biên dịch. Quy tắc macro xác định các macro tùy chỉnh phù hợp với bất kỳ biểu thức hoặc câu lệnh gỉ nào.
Bạn sẽ sử dụng các quy tắc macro để tạo mã soạn sẵn để trừu tượng hóa các chi tiết cấp thấp.
Đây là cách bạn có thể xác định và sử dụng các quy tắc macro trong các chương trình Rust của mình:
macro_rules! make_vector {
( $( $x:expr ),* ) => {
{
let mut v = Vec::new();
$(
v.push($x);
)*
v
}
};
}fn main() {
let v = make_vector![1, 2, 3];
println!("{:?}", v);
}
Chương trình xác định một make_vector! một macro tạo một vectơ mới từ danh sách các biểu thức được phân tách bằng dấu phẩy trong chủ yếu chức năng.
Bên trong macro, định nghĩa mẫu khớp với các đối số được truyền cho macro. Các $( $x:expr ),* cú pháp khớp với bất kỳ biểu thức được phân tách bằng dấu phẩy nào được xác định là $x.
Các $( ) cú pháp trong mã mở rộng lặp qua từng biểu thức trong danh sách các đối số được truyền tới macro sau dấu ngoặc đơn đóng, cho biết rằng các phép lặp sẽ tiếp tục cho đến khi macro xử lý tất cả các biểu thức.
Tổ chức các dự án Rust của bạn một cách hiệu quả
Macro Rust cải thiện tổ chức mã bằng cách cho phép bạn xác định các mẫu mã có thể tái sử dụng và trừu tượng hóa. Macro có thể giúp bạn viết mã ngắn gọn, biểu cảm hơn mà không bị trùng lặp giữa các phần khác nhau của dự án.
Ngoài ra, bạn có thể tổ chức các chương trình Rust thành các thùng và mô-đun để tổ chức mã tốt hơn, khả năng sử dụng lại và tương tác với các thùng và mô-đun khác.