はじめに
以下のようなことをやりたいです。
mod A {
#[derive(Default)]
pub struct Foo {
pub id: i64,
pub name: String,
}
}
mod B {
pub struct Foo {
pub id: i64,
pub name: String,
}
}
let a = A::Foo::default();
let b = B::Foo { id: 1, ..a };
assert_eq!(a, b);
上記の例でいうと、fooとbarが同じ型であれば問題なくコンパイルが通ります。
しかしジェネリクスが使用されていたり、名前空間が異なると異なる型とみなされて実現できません。
調べてみるとRFCがありましたがFurther generalization
として提示されています。
(ジェネリクスについては1.58あたりで入りそうです)
環境
❯ rustc -V
rustc 1.55.0 (c8dfcfe04 2021-09-06)
成果
use type_change::From;
# [derive(Clone)]
struct Foo {
id: i64,
name: String,
}
# [derive(From)]
# [from(Foo)]
struct Bar {
id: i64,
name: String,
}
let foo = Foo { id: 1, name: "foo".to_string() };
let bar = Bar { name: "bar".to_string(), ..foo.clone().into() };
assert_eq!(foo.id, bar.id);
リポジトリはこちらです
タプル構造体や構造体のフィールドでも変換できます。
現状はすべてのフィールド名と型が一致しないと変換できないのですが、attributeを付けてフィールドごとに制御できたらもっと使いやすいと思います。
終わりに
マクロは初めて書いたのですがsynとquoteのおかげで直感的にかなり書きやすいと思いました。
手続きマクロは別クレートを用意しなければならないのであまり気軽に書くことはなさそうですが、何でもできるので覚えておくと困ったときに役立つと思います。
参考