Làm cách nào để sử dụng macro trên các tệp mô-đun?


93

Tôi có hai mô-đun trong các tệp riêng biệt trong cùng một thùng, nơi thùng đã macro_rulesđược kích hoạt. Tôi muốn sử dụng các macro được xác định trong một mô-đun trong một mô-đun khác.

// macros.rs
#[macro_export] // or not? is ineffectual for this, afaik
macro_rules! my_macro(...)

// something.rs
use macros;
// use macros::my_macro; <-- unresolved import (for obvious reasons)
my_macro!() // <-- how?

Tôi hiện gặp lỗi trình biên dịch " macro undefined: 'my_macro'" ... có lý; hệ thống macro chạy trước hệ thống mô-đun. Làm cách nào để giải quyết vấn đề đó?


Shouldn; không bạn sử dụngmodule::my_macro!()?
u_mulder

2
nope (không phải afaik) - tiền tố mô-đun được báo cáo là bị bỏ qua (theo thông báo của trình biên dịch).
người dùng

Câu trả lời:


131

Macro trong cùng một thùng

#[macro_use]
mod foo {
    macro_rules! bar {
        () => ()
    }
}

bar!();    // works

Nếu bạn muốn sử dụng macro trong cùng một thùng, mô-đun mà macro của bạn được xác định cần thuộc tính #[macro_use].

Macro chỉ có thể được sử dụng sau khi chúng đã được xác định. Điều này có nghĩa là điều này không hoạt động:

bar!();  // ERROR: cannot find macro `bar!` in this scope

#[macro_use]
mod foo {
    macro_rules! bar {
        () => ()
    }
}

Macro trên các thùng

Để sử dụng macro_rules!macro của bạn từ các thùng khác, bản thân macro cần thuộc tính #[macro_export]. Sau đó, thùng nhập có thể nhập macro qua use crate_name::macro_name;.

Thùng util

#[macro_export]
macro_rules! foo {
    () => ()
}

Thùng user

use util::foo;

foo!();

Lưu ý rằng macro luôn ở cấp cao nhất của thùng; vì vậy ngay cả khi foosẽ ở bên trong a mod bar {}, userthùng vẫn phải ghi use util::foo;không use util::bar::foo; .

Trước Rust 2018, bạn phải nhập macro từ các thùng khác bằng cách thêm thuộc tính #[macro_use]vào extern crate util;câu lệnh. Điều đó sẽ nhập tất cả các macro từ util. Ngoài ra, #[macro_use(cat, dog)]có thể được sử dụng để chỉ nhập các macro catdog. Cú pháp này không cần thiết nữa.

Thông tin thêm có sẵn trong chương Ngôn ngữ lập trình Rust về macro .


27
"Macro chỉ có thể được sử dụng sau khi chúng đã được xác định." - Đây là chìa khóa vì bạn có thể gặp phải lỗi đó ngay cả khi bạn đã thực hiện tất cả những điều khác được đề cập một cách chính xác. Ví dụ: nếu bạn có các mô-đun macrosfoo(sử dụng macro từ macros) và bạn liệt kê chúng theo thứ tự bảng chữ cái trong lib.rs hoặc main.rs, foo sẽ được tải trước macro và mã sẽ không được biên dịch.
neverfox

7
^ mẹo chuyên nghiệp - điều này hoàn toàn có lợi cho tôi
semore_1267

3
Cũng lưu ý rằng để sử dụng macro trong nội bộ, #[macro_use]thuộc tính phải có trên mọi mô-đun và mô-đun mẹ, v.v. cho đến khi nó đạt đến điểm bạn cần sử dụng.
Mười

Câu trả lời này không làm việc cho tôi. Mô-đun khai báo macro có #[macro_use]và nó được khai báo đầu tiên trong lib.rs - vẫn không hoạt động. Câu trả lời của @ Ten đã giúp ích và tôi đã thêm vào #[macro_use]đầu lib.rs - sau đó nó hoạt động. Nhưng tôi vẫn không chắc thực tiễn tốt nhất là gì vì tôi đọc ở đây rằng "Bạn không nhập macro từ các mô-đun khác; bạn xuất macro từ mô-đun xác định"
Sorin Bolos

Tôi luôn quên cách macro của Rust hoạt động với các mô-đun. Đó là một hệ thống tồi tệ, và hy vọng một ngày nào đó sẽ có một hệ thống tốt hơn.
Hutch Moore

20

Câu trả lời này đã lỗi thời kể từ Rust 1.1.0-ổn định.


Bạn cần thêm #![macro_escape]ở đầu macros.rsvà thêm nó bằng cách sử dụng mod macros;như đã đề cập trong Hướng dẫn Macro .

$ cat macros.rs
#![macro_escape]

#[macro_export]
macro_rules! my_macro {
    () => { println!("hi"); }
}

$ cat something.rs
#![feature(macro_rules)]
mod macros;

fn main() {
    my_macro!();
}

$ rustc something.rs
$ ./something
hi

Để tham khảo trong tương lai,

$ rustc -v
rustc 0.13.0-dev (2790505c1 2014-11-03 14:17:26 +0000)

Tôi hoàn toàn bỏ lỡ thuộc tính đó. Cảm ơn!
người dùng

4
BTW, #[macro_export]thuộc tính là không cần thiết ở đây. Nó chỉ cần thiết nếu macro được xuất cho người dùng thùng bên ngoài. Nếu macro chỉ được sử dụng bên trong thùng, #[macro_export]thì không cần thiết.
Vladimir Matveev

1
Cảm ơn rất nhiều cho câu trả lời. Tôi chỉ muốn nói thêm rằng nếu something.rstệp của bạn sử dụng các mô-đun khác, chẳng hạn với mod foobar;foobarmô-đun này sử dụng các macro từ macro.rs, thì bạn phải đặt mod macro; trước mod foobar; để chương trình biên dịch. Điều nhỏ nhặt, nhưng đây không phải là IMO rõ ràng.
conradkleine Phúc âm

2
(nb câu trả lời này hiện đã lỗi thời; tôi đã chấp nhận câu trả lời cập nhật do Lukas đưa ra)
người dùng

7

Thêm #![macro_use]vào đầu tệp của bạn có chứa macro sẽ khiến tất cả các macro được kéo vào main.rs.

Ví dụ: giả sử tệp này được gọi là node.rs:

#![macro_use]

macro_rules! test {
    () => { println!("Nuts"); }
}

macro_rules! best {
    () => { println!("Run"); }
}

pub fn fun_times() {
    println!("Is it really?");
}

Đôi khi main.rs của bạn sẽ trông giống như sau:

mod node;  //We're using node.rs
mod toad;  //Also using toad.rs

fn main() {
    test!();
    best!();
    toad::a_thing();
}

Cuối cùng, giả sử bạn có một tệp có tên là kid.rs cũng yêu cầu các macro này:

use node; //Notice this is 'use' not 'mod'

pub fn a_thing() {
  test!();

  node::fun_times();
}

Lưu ý rằng khi các tệp được kéo vào main.rs bằng mod, phần còn lại của các tệp của bạn có quyền truy cập vào chúng thông qua usetừ khóa.


Tôi đã bổ sung làm rõ hơn. Kể từ gỉc 1.22.1, điều này hoạt động.
Luke Dupin

Bạn có chắc không? #! [Macro_use] (không phải # [macro_use]) này được ghi lại ở đâu? Tôi không thể tìm thấy nó. Nó không hoạt động ở đây.
Markus

Điều này đã hoạt động khi tôi đăng nó, hệ thống bao gồm của Rust thật là một mớ hỗn độn khủng khiếp, hoàn toàn có thể điều này không hoạt động nữa.
Luke Dupin

@Markus Lưu ý rằng #![macro_use]câu lệnh nằm BÊN TRONG mô-đun macro, không phải bên ngoài. Các #![...]tương ứng với cú pháp để có thuộc tính áp dụng cho phạm vi chứa của họ, ví dụ như #![feature(...)](rõ ràng điều này sẽ không có ý nghĩa nếu viết như #[feature(...)], nó sẽ ngữ nghĩa yêu cầu trình biên dịch cho phép tính năng nhất định vào các mặt hàng cụ thể trong một thùng, chứ không phải là thùng gốc toàn bộ). Vì vậy, như @LukeDupin đã nói, hệ thống mô-đun là một mớ hỗn độn, mặc dù có thể vì một lý do khác so với cái nhìn đầu tiên.
người dùng

Tôi ước rằng câu trả lời này đề cập đến cách xây dựng không chính xác thành ngữ (điều đó sang một bên, tôi thích câu trả lời). Bất chấp tính chất (không) của nó, thật thú vị vì việc đặt nó bên cạnh dạng thành ngữ làm cho nó rõ ràng là macro tương tác với hệ thống mô-đun theo một cách khác với các cấu trúc thông thường. Hoặc ít nhất nó cũng tỏa ra một mùi nồng nặc (như vừa được chứng minh bởi @Markus đã rất thích nó).
người dùng

2

Tôi đã gặp vấn đề tương tự trong Rust 1.44.1 và giải pháp này hoạt động cho các phiên bản sau (được biết là hoạt động cho Rust 1.7).

Giả sử bạn có một dự án mới là:

src/
    main.rs
    memory.rs
    chunk.rs

Trong main.rs , bạn cần phải chú thích rằng bạn đang nhập macro từ nguồn, nếu không, nó sẽ không làm cho bạn.

#[macro_use]
mod memory;
mod chunk;

fn main() {
    println!("Hello, world!");
}

Vì vậy, trong memory.rs, bạn có thể xác định các macro và bạn không cần chú thích:

macro_rules! grow_capacity {
    ( $x:expr ) => {
        {
            if $x < 8 { 8 } else { $x * 2 }
        }
    };
}

Cuối cùng, bạn có thể sử dụng nó trong chunk.rs và bạn không cần bao gồm macro ở đây, vì nó được thực hiện trong main.rs:

grow_capacity!(8);

Các câu trả lời upvoted gây nhầm lẫn đối với tôi, với doc này bằng ví dụ , nó sẽ là hữu ích quá.


Câu trả lời được chấp nhận theo nghĩa đen có mà như các dòng đầu tiên của khối mã đầu tiên: #[macro_use] mod foo {.
Shepmaster

1
@Shepmaster câu trả lời ủng hộ có định nghĩa về macro và câu lệnh nhập ở cùng một nơi, vì vậy nó gây ra sự nhầm lẫn (đối với tôi). Tôi đã sử dụng #[macro_use]trong định nghĩa. Trình biên dịch không nói rằng nó được đặt sai vị trí.
knh190

Bạn có thể muốn đọc lại doc.rust-lang.org/book/… sau đó.
Shepmaster

Cảm ơn bạn vì câu trả lời này! Tôi cũng bối rối trước câu trả lời được chấp nhận và không thể tìm ra nó cho đến khi tôi đọc lời giải thích của bạn.
Prgrm.celeritas

@Shepmaster Không có đề cập đến cách hoạt động của macro trong phần bạn liên kết đến. Ý của bạn là liên kết đến một số phần khác của cuốn sách?
detly
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.