RustではTSのUtility Typesのように既存の型から新しい型を導出できるか?(疑問)
(マクロではない)Rustの基本構文ではどうやら出来なさそう。マクロを使えば何となくできる気がすると考えているがどうか?マクロの仕様を全く理解してないので、マクロでどこまでやれるのかがイメージできない。
マクロのお勉強をしたいが、そもそもRust自体まだまだ学習中なのでマクロに手を出すのがあまりにも時期尚早すぎるんですよね。ということでそのうち調べるということで忘れないようにメモ。
ちょっと検索したら、そのまんまのものを実装している方を発見した。
後で動作確認とソース調べてみる。
use utility_types::omit;
#[omit(CreateUserRequest, [id], [Debug])]
pub struct User {
pub id: i32,
pub name: String,
pub email: String
}
pub fn main() {
let user = CreateUserRequest {
name: "John".to_string(),
email: "john@example.com".to_string()
};
println!("{:?}", user);
}
実行結果:
$ cargo run
CreateUserRequest { name: "John", email: "john@example.com" }
ちゃんと動くね。
partialも試す。
use utility_types::partial;
#[partial(PartialUser)]
pub struct User {
pub id: i32,
pub name: String,
pub email: String
}
pub fn main() {
let user = PartialUser {
id: Some(1),
name: Some("John".to_string()),
email: None
};
}
ちゃんと動いてますね。
※omitの時は[Debug]を指定できたのにpartialには指定できなかった。
ソースちゃんと読めてないので理由不明です。
マクロを連続で適用して結果を得ることができないっぽい。
つまり、次のpartialとomitを同時に指定しても、
#[partial(UpdateUserRequest)]
#[omit(CreateUserRequest, [id])]
pub struct User {
pub id: i32,
pub name: String,
pub email: String
}
下記のような型は生成されない模様。
pub struct UpdateUserRequest {
pub name: Option<String>,
pub email: Option<String>
}
Userに対して、それぞれparitlとomitを適用した型が生成されるだけでした。
とりあえず頑張れば多分どうにかできるだろうという感触を得ることはできました。
マクロについてはそのうちちゃんと調べて記事にします。
マクロについて簡単にまとめておいてクローズします。
Rustのマクロは大きく分けて二つに分かれる。
- 宣言的マクロ
- 手続き的マクロ
宣言的マクロはmacro_rules!で定義する。
入力を受け取りテンプレートに入力値を当て込んだRustコードを返すもの。
手続き的マクロはロジックに基づいてRustコードを組み立てて返すことができる。
(抽象構文木を受け取り、新しい抽象構文木を返す)
つまりコンパイル時に任意の処理を実行できる。 ここでいう任意の処理とは、
- 構造体に任意のメソッドを実装してトレイトの要件を満たす
- データベースに接続してSQL文に不正がないか検出する(sqlxの静的コンパイル検証)
などがある。手続きマクロは、さらに3つの種類に分けることができる。
- proc_macro: 関数呼び出し形式で使うマクロ
- proc_macro_attribute: アトリビュートで指定して使うマクロ
- proc_macro_derive: #[derive(...)]で指定するマクロ
今回の例では構造体にアトリビュートを付与して新しい型を生成しているので、proc_macro_attributeに該当する。