2014-07-01 26 views
6

Tôi đang cố gắng để tạo ra một macro trong Rust cho phép tôi viếtLàm thế nào để sử dụng các macro variadic để gọi các hàm tạo lồng nhau?

make_list!(1, 2, 3) 

thay vì

Node::new(1, Node::new(2, Node::new(3, None))) 

mà nên làm việc cho một số tùy ý các "thông số" bao gồm zero. Đây là những gì tôi có cho đến nay:

macro_rules! make_list(
    () => (
     None 
    ); 
     ($x:expr, $($more:expr),*) => (
     Node::new($x, make_list!($($more),*)) 
    ) 
); 

nhưng tôi nhận được lỗi sau:

error: unexpected end of macro invocation 
    --> src/main.rs:19:42 
    | 
19 |    Node::new($x, make_list!($($more),*)) 
    |           ^^^^^ 

tôi không thể có ý nghĩa nhiều về điều này. Từ những gì tôi có thể nói, nó sẽ hoạt động. Tôi đã làm gì sai?

Các mã hoàn chỉnh:

type List<T> = Option<Box<Node<T>>>; 

struct Node<T> { 
    value: T, 
    tail: List<T>, 
} 

impl<T> Node<T> { 
    fn new(val: T, tai: List<T>) -> List<T> { 
     Some(Box::new(Node::<T> { 
      value: val, 
      tail: tai, 
     })) 
    } 
} 

macro_rules! make_list(
    () => (
     None 
    ); 
    ($x:expr, $($more:expr),*) => (
     Node::new($x, make_list!($($more),*)) 
    ) 
); 

fn main() { 
    let _list: List<i32> = make_list!(1, 2, 3, 4, 5, 6, 7, 8, 9); 
} 
+0

Macro của bạn nhận 0 hoặc 2 đối số nhưng bạn chỉ chuyển nó 1 – Arjan

+1

@Arjan: Nhưng tôi nghĩ đó là những gì $ (...), * dành cho. Nó phải khớp không với nhiều đối số hơn. Có nên không? – sellibitze

Trả lời

8

Mở rộng trên các lỗi: bạn nhận được xuống để trường hợp chỉ có một giá trị, và vì vậy nó viết make_list!(1). Tuy nhiên, không có quy tắc nào khớp với quy tắc thứ hai, sau khi tiêu thụ biểu thức x, muốn có dấu phẩy, không được cung cấp.

Vì vậy, bạn cần phải làm cho nó để nó sẽ làm việc cho make_list!(1) và không chỉ (trên thực tế, chỉ không) make_list!(1,). Để đạt được điều này, có dấu phẩy bên trong phần lặp đi lặp lại, như thế này:

macro_rules! make_list(
    () => (
     None 
    ); 
    ($x:expr $(, $more:expr)*) => (
     Node::new($x, make_list!($($more),*)) 
    ) 
); 

Bonus: bạn có thể viết make_list![1, 2, 3] thay vì make_list!(1, 2, 3) nếu bạn muốn.

+0

Cảm ơn! Tôi đoán tôi đã quá quen với phép thuật của các mẫu C++ variadic. :) – sellibitze

+1

Cách tiếp cận này không còn hoạt động; xem [tại đây] (https://users.rust-lang.org/t/tail-recursive-macros/905/3). – bgilbert

+0

Phần nào của câu trả lời này không còn hoạt động? – ideasman42

0

Như được lưu ý bởi câu trả lời @ chris-morgan, việc mở rộng trường hợp đối số đơn lẻ không được tính.

Vì vậy, bạn có thể bao gồm dấu phẩy trong việc mở rộng, hoặc thêm một trường hợp duy nhất trong macro:

Ví dụ về cả hai, lập luận duy nhất:

macro_rules! make_list { 
    () => (
     None 
    ); 
    ($x:expr) => (
     Node::new($x, None) 
    ); 
    ($x:expr, $($more:expr),+) => (
     Node::new($x, make_list!($($more),*)) 
    ); 
} 

Bao gồm các dấu phẩy trong việc mở rộng:

macro_rules! make_list { 
    () => (
     None 
    ); 
    ($x:expr $(, $more:expr)*) => (
     Node::new($x, make_list!($($more),*)) 
    ); 
} 

Dưới đây là ví dụ hoàn toàn hoạt động dựa trên câu hỏi và được cập nhật cho Rust 1.14:

type List<T> = Option<Box<Node<T>>>; 

#[derive(Debug)] 
struct Node<T> { 
    value: T, 
    tail: List<T> 
} 

impl<T> Node<T> { 
    fn new(val: T, tai: List<T>) -> List<T> { 
     Some(Box::new(Node::<T> { value: val, tail: tai })) 
    } 
} 

macro_rules! make_list { 
    () => (
     None 
    ); 
    ($x:expr $(, $more:expr)*) => (
     Node::new($x, make_list!($($more),*)) 
    ); 
} 

fn main() { 
    let list: List<i64> = make_list!(); 
    println!("{:?}", list); 
    let list: List<i64> = make_list!(1); 
    println!("{:?}", list); 
    let list: List<i64> = make_list!(1,2,3,4,5,6,7,8,9); 
    println!("{:?}", list); 
} 
Các vấn đề liên quan