65469


このコードを通すにはどうすればよいでしょうか

[code]

    mod module {
        pub trait Trait<T> {}
    }
    
    struct Type();
    
    macro_rules! impl_trait {
        ($trait:path,$type:ty) => {
            impl $trait<i32> for $type {}
    //                       ~~~ expected `::`
        };
    }
    
    impl_trait!(module::Trait, Type);
    
[/code]


CC BY-SA 4.0
2020-04-08T11:21:20.360
65468
2020-04-09T09:06:21.800
20885
question
rust
use していないモジュール内のジェネリックトレイトをマクロで指定して実装したい


`path` だとあとから要素を足せなくなるので `tt` を使うとコンパイルが通ります。  
このときに `$(tt)*` の後ろにカンマがあるとパースエラーになるので `path` の方を最後にもってくる必要があります。

[code]

    mod module {
        pub trait Trait<T> {}
    }
    
    struct Type();
    
    macro_rules! impl_trait {
        ($type: ty, $($trait:tt)*) => {
            impl $($trait)* ::<i32> for $type {}
        }
    }
    
    impl_trait!(Type, module::Trait);
    
    fn main() {}
    
[/code]


CC BY-SA 4.0
2020-04-08T12:01:31.677
65469
2020-04-08T12:01:31.677
22816
65468
answer


別解として、`paste` というクレートを用いた解決策を紹介します。

<https://github.com/dtolnay/paste>

`paste` は元々不安定な組み込みマクロである [`concat_idents!()`](https://doc.rust-
lang.org/std/macro.concat_idents.html)
を安定板のコンパイラで模倣するためのクレートであり、手続き的マクロを用いて実装されています。手続き的マクロを経由する際に `TokenStream`
への変換が挟まれることで本来マクロのメタ変数が持っているフラグメント指定子が失われ、今回の例のような通常のマクロでは許容されない結合を行うことが出来るようになります。

今回の場合生成されるコードはトレイトの実装であるため、`paste::item!` で生成したいコード片を囲むことでコンパイルが通るようになります。

[code]

    macro_rules! impl_trait {
        ($trait:path, $type:ty) => {
            paste::item! {
                impl $trait<i32> for $type {}
            }
        };
    }
    
[/code]


CC BY-SA 4.0
2020-04-09T09:06:21.800
65492
2020-04-09T09:06:21.800
24530
65468
answer