PrAhaPublicationへの投稿😺RustでAPIを開発してみたら結構辛かった話Kyosuke Awata2023/02/20に公開2023/02/2715件RustAPIDDDドメイン駆動設計モジュラモノリスtechGitHubで編集を提案PrAhaPublication株式会社PrAhaでは、株式会社アガルートのサービス「アガルートアカデミー」の開発、自社サービス「PrAha Challenge」の運営、スタートアップに特化したデザインと受託開発を行なっています。一緒に働いてくれる方を血眼になって探しています。Discussionkbwo2023/02/21に更新(以下で言うORMはいわゆるActiveRecord的なものを指します) ORMについて、多様性に欠けるという点ではエコシステムが充実していないというのはその通りだと思いますが、発展が遅く未熟というよりは、Rust開発者がORMを好まない傾向にあるためORM開発のモチベーションが比較的少ないというのもあるのではないかなと思っています。 明確なエビデンスがあるわけではないですが、Redditを観察しているとRustのORMやDB周りの話では毎回sqlx推しが集まっている印象があります。それもdieselが非同期処理をサポートしていないなどによる消去法的選択肢というよりは、ORMを使うことによる短期的メリットより、sqlxのような薄いながらもコンパイル時チェックなど本当に解決してほしい問題を解決してくれるものを推すようなコメントが多いように思います。 ただ、私もsqlx推しのためそのバイアスがかかっていますし、"dieselは使いたくないけど、かといってRustのエコシステムが未熟なことを認めたくない"だけのRust開発者もいるかもしれないので、上記の印象は眉唾ものですが、こういう視点もあるというコメントでした。 Kyosuke Awata2023/02/21コメントありがとうございます! Rust開発者の傾向まではチェックできていなかったので、大変参考になります! 返信を追加鏡華2023/02/21に更新chronoはserdeのSerialize/Deserialize実装を提供しています https://github.com/chronotope/chrono/blob/main/src/datetime/serde.rs Rustは言語機能としてfeature-gateを内蔵しており、ライブラリなどにおいても使う側が必要な機能だけを有効にすることができます. serdeでのシリアライズ機能は、chronoの利用者のすべてが必要としているわけではないが、あると便利、みたいなものですよね. なのでデフォルトでは無効にしつつ、featureを指定した場合に有効になる、のように実装されています. Cargo.tomlで [dependencies] chrono = { version = "0.4.23", features = ["serde"] } などとすればSerialize/Deserialize出来るようになるはずです Kyosuke Awata2023/02/21ありがとうございます! こちらのやり方の方が簡単なので、もっと簡単なやり方もあるよ!と追記しようと思います! 返信を追加鏡華2023/02/21に更新 可変参照を色々なところで使わざるを得ない状況になってしまった これは内部可変性といわれるパターンで解消できます. 記事内に書かれているRefCellも内部可変性を実現するための構造体のうちの一つです. RcやRefCellはSendやSyncを実装していない(スレッドを跨ぐマルチスレッド環境では安全に扱えない)ので、Webサーバーなどマルチスレッドな環境では安全に扱えるArc<Mutex<T>>などが頻出します. 返信を追加magicant2023/02/21 ユースケースやドメインサービスにこのコネクションオブジェクトを渡す必要がありました。 普通、データベースへのコネクションは処理のスレッドごとに別々に用意するものじゃないでしょうか。複数の処理で同時に同じコネクションを無理やり使い回すと、あるトランザクションの途中で別のトランザクションの処理が混じるみたいなカオスな状況になりそうな気がします。 コネクションを毎回何度も接続したり切断したりするのは非効率なのでコネクションプールを使って管理するのが一般的かと思いますがいかがでしょう。 Kyosuke Awata2023/02/21コメントありがとうございます! 普通、データベースへのコネクションは処理のスレッドごとに別々に用意するものじゃないでしょうか。複数の処理で同時に同じコネクションを無理やり使い回すと、あるトランザクションの途中で別のトランザクションの処理が混じるみたいなカオスな状況になりそうな気がします。 コネクションを毎回何度も接続したり切断したりするのは非効率なのでコネクションプールを使って管理するのが一般的かと思いますがいかがでしょう。 その通りだと思います! そして今回もそのような作りになっています! 具体的には以下のような流れになります(詳細はサンプルコードを読んで頂けると分かりやすいかと思います) scenarioでコネクションプールからコネクションオブジェクトを取得 scenarioからpresentationに定義されている関数を呼び出す presentationの関数では、ユースケースのインスタンスを生成する(必要ならリポジトリやドメインサービスを生成してユースケースに渡す。 ...省略... ↑このリポジトリやドメインサービスを作る際に可変参照なコネクションオブジェクトが必要になります magicant2023/02/21返信ありがとうございます。そして本文のコードをうまく読み解けていなかったようですみません。 コネクションプールは既に使っておられて、コネクションプールからコネクションを取り出すまでは順調にできているのですね。それでいて RcとRefCellを組み合わせて無理やり可変参照を複数の場所から参照できるようにしました ということが必要だったということは、ユースケースやドメインサービスの中にコネクションの参照を保持しているということでしょうか。コネクションを保持するオブジェクトが複数あるのであれば一つのコネクションを共有するために Rc<RefCell> のパターンが必要になってくるのも腑に落ちます。 ただ自分ならオブジェクト内に保持するのではなくて必要になるたびに毎回関数の引数で渡す道を選びそうだなと思いました。その方がコンパイル時チェックに頼れる範囲が広いので。 返信を追加白山風露2023/02/21From トレイトの話をするなら Into トレイトの話を入れてほしかった感。 From トレイトの嬉しい点は From トレイトを実装すると Into トレイトが自動的に実装されて、 into() で気軽に型変換できることだと思うので、 X::from(y) の形で使用することだけだと本当になんかちょっと短くなっただけでX::from_y(y) を実装するのと大差なくなってしまう 返信を追加Takaaki Furuse2023/02/23(マジレスでなく、ジョークとして読んでください。) RobynというPython製のフレームワークがありまして・・・ これを使うという反則技もあります。 下ではPyO3というライブラリーを使ってAPIレベルからPythonコードをRustに変換してる感じです。 https://robyn.tech/ Takaaki Furuse2023/02/23下で動いてるのはRustなので、Rustで開発してる!(キリッ 返信を追加フラワー2023/02/26突然の質問失礼致します。 気になったのですが、ユースケース層やドメイン層にDBコネクションオブジェクトを持たせる理由は何でしょうか? 基本的なDDDのプラクティスであれば、ドメインロジックとDBやFW等の詳細を分離するため、DBコネクションはインフラストラクチャ層で実装するものと理解しています。ユースケース層やドメイン層にDBコネクションを渡した場合、この分離ができなくなると思うのですが、もし何か理由があれば教えていただきたいです。(自分の理解が誤ってるかもしれないので、その場合は指摘いただけると幸いです。) Kyosuke Awata2023/02/27これは記事の内容が誤ってました、申し訳ありません。 正しくは「複数のリポジトリのコンストラクタにコネクションオブジェクトを渡す必要がある」でした。 ユースケースやドメインサービスは、生成されたリポジトリを受け取る形になりますね。 記事も併せて修正しました!ありがとうございます! 具体的なコードはこちらが分かりやすいかと思います。 フラワー2023/03/05いえいえ、回答ありがとうございます!そして、返信遅くなり申し訳ありません。 DBコネクションをリポジトリに渡すということであれば、自分のDDDの理解とずれてないので納得できました。 余談ですが、ソースコード読ませていただきました。なるほど、複数の機能でDBコネクションオブジェクトを使いまわしたいから、それぞれの機能のリポジトリにDBコネクションを渡さなければならず、複数のリポジトリのコンストラクタにコネクションオブジェクトを渡す必要があるということですね。 それで、DBコネクションは可変参照だから shared xor mutableで複数オブジェクトで共有できないと。 確かにGCありの言語なら、DB接続はインスタンス変数に持たせて使いまわしたりすることが常套手段だと思いますが、Rustだとその辺りが厄介になりそうなところかもですね。 僕も勉強になりました。ありがとうございました。 返信を追加Toshiki2025/03/06ご存知かもしれませんが、 JSONからRustの構造体を作りたいときはこのサイトを使うと楽になります! https://transform.tools/json-to-rust-serde 返信を追加
kbwo2023/02/21に更新(以下で言うORMはいわゆるActiveRecord的なものを指します) ORMについて、多様性に欠けるという点ではエコシステムが充実していないというのはその通りだと思いますが、発展が遅く未熟というよりは、Rust開発者がORMを好まない傾向にあるためORM開発のモチベーションが比較的少ないというのもあるのではないかなと思っています。 明確なエビデンスがあるわけではないですが、Redditを観察しているとRustのORMやDB周りの話では毎回sqlx推しが集まっている印象があります。それもdieselが非同期処理をサポートしていないなどによる消去法的選択肢というよりは、ORMを使うことによる短期的メリットより、sqlxのような薄いながらもコンパイル時チェックなど本当に解決してほしい問題を解決してくれるものを推すようなコメントが多いように思います。 ただ、私もsqlx推しのためそのバイアスがかかっていますし、"dieselは使いたくないけど、かといってRustのエコシステムが未熟なことを認めたくない"だけのRust開発者もいるかもしれないので、上記の印象は眉唾ものですが、こういう視点もあるというコメントでした。 Kyosuke Awata2023/02/21コメントありがとうございます! Rust開発者の傾向まではチェックできていなかったので、大変参考になります! 返信を追加
鏡華2023/02/21に更新chronoはserdeのSerialize/Deserialize実装を提供しています https://github.com/chronotope/chrono/blob/main/src/datetime/serde.rs Rustは言語機能としてfeature-gateを内蔵しており、ライブラリなどにおいても使う側が必要な機能だけを有効にすることができます. serdeでのシリアライズ機能は、chronoの利用者のすべてが必要としているわけではないが、あると便利、みたいなものですよね. なのでデフォルトでは無効にしつつ、featureを指定した場合に有効になる、のように実装されています. Cargo.tomlで [dependencies] chrono = { version = "0.4.23", features = ["serde"] } などとすればSerialize/Deserialize出来るようになるはずです Kyosuke Awata2023/02/21ありがとうございます! こちらのやり方の方が簡単なので、もっと簡単なやり方もあるよ!と追記しようと思います! 返信を追加
鏡華2023/02/21に更新 可変参照を色々なところで使わざるを得ない状況になってしまった これは内部可変性といわれるパターンで解消できます. 記事内に書かれているRefCellも内部可変性を実現するための構造体のうちの一つです. RcやRefCellはSendやSyncを実装していない(スレッドを跨ぐマルチスレッド環境では安全に扱えない)ので、Webサーバーなどマルチスレッドな環境では安全に扱えるArc<Mutex<T>>などが頻出します. 返信を追加
magicant2023/02/21 ユースケースやドメインサービスにこのコネクションオブジェクトを渡す必要がありました。 普通、データベースへのコネクションは処理のスレッドごとに別々に用意するものじゃないでしょうか。複数の処理で同時に同じコネクションを無理やり使い回すと、あるトランザクションの途中で別のトランザクションの処理が混じるみたいなカオスな状況になりそうな気がします。 コネクションを毎回何度も接続したり切断したりするのは非効率なのでコネクションプールを使って管理するのが一般的かと思いますがいかがでしょう。 Kyosuke Awata2023/02/21コメントありがとうございます! 普通、データベースへのコネクションは処理のスレッドごとに別々に用意するものじゃないでしょうか。複数の処理で同時に同じコネクションを無理やり使い回すと、あるトランザクションの途中で別のトランザクションの処理が混じるみたいなカオスな状況になりそうな気がします。 コネクションを毎回何度も接続したり切断したりするのは非効率なのでコネクションプールを使って管理するのが一般的かと思いますがいかがでしょう。 その通りだと思います! そして今回もそのような作りになっています! 具体的には以下のような流れになります(詳細はサンプルコードを読んで頂けると分かりやすいかと思います) scenarioでコネクションプールからコネクションオブジェクトを取得 scenarioからpresentationに定義されている関数を呼び出す presentationの関数では、ユースケースのインスタンスを生成する(必要ならリポジトリやドメインサービスを生成してユースケースに渡す。 ...省略... ↑このリポジトリやドメインサービスを作る際に可変参照なコネクションオブジェクトが必要になります magicant2023/02/21返信ありがとうございます。そして本文のコードをうまく読み解けていなかったようですみません。 コネクションプールは既に使っておられて、コネクションプールからコネクションを取り出すまでは順調にできているのですね。それでいて RcとRefCellを組み合わせて無理やり可変参照を複数の場所から参照できるようにしました ということが必要だったということは、ユースケースやドメインサービスの中にコネクションの参照を保持しているということでしょうか。コネクションを保持するオブジェクトが複数あるのであれば一つのコネクションを共有するために Rc<RefCell> のパターンが必要になってくるのも腑に落ちます。 ただ自分ならオブジェクト内に保持するのではなくて必要になるたびに毎回関数の引数で渡す道を選びそうだなと思いました。その方がコンパイル時チェックに頼れる範囲が広いので。 返信を追加
Kyosuke Awata2023/02/21コメントありがとうございます! 普通、データベースへのコネクションは処理のスレッドごとに別々に用意するものじゃないでしょうか。複数の処理で同時に同じコネクションを無理やり使い回すと、あるトランザクションの途中で別のトランザクションの処理が混じるみたいなカオスな状況になりそうな気がします。 コネクションを毎回何度も接続したり切断したりするのは非効率なのでコネクションプールを使って管理するのが一般的かと思いますがいかがでしょう。 その通りだと思います! そして今回もそのような作りになっています! 具体的には以下のような流れになります(詳細はサンプルコードを読んで頂けると分かりやすいかと思います) scenarioでコネクションプールからコネクションオブジェクトを取得 scenarioからpresentationに定義されている関数を呼び出す presentationの関数では、ユースケースのインスタンスを生成する(必要ならリポジトリやドメインサービスを生成してユースケースに渡す。 ...省略... ↑このリポジトリやドメインサービスを作る際に可変参照なコネクションオブジェクトが必要になります
magicant2023/02/21返信ありがとうございます。そして本文のコードをうまく読み解けていなかったようですみません。 コネクションプールは既に使っておられて、コネクションプールからコネクションを取り出すまでは順調にできているのですね。それでいて RcとRefCellを組み合わせて無理やり可変参照を複数の場所から参照できるようにしました ということが必要だったということは、ユースケースやドメインサービスの中にコネクションの参照を保持しているということでしょうか。コネクションを保持するオブジェクトが複数あるのであれば一つのコネクションを共有するために Rc<RefCell> のパターンが必要になってくるのも腑に落ちます。 ただ自分ならオブジェクト内に保持するのではなくて必要になるたびに毎回関数の引数で渡す道を選びそうだなと思いました。その方がコンパイル時チェックに頼れる範囲が広いので。
白山風露2023/02/21From トレイトの話をするなら Into トレイトの話を入れてほしかった感。 From トレイトの嬉しい点は From トレイトを実装すると Into トレイトが自動的に実装されて、 into() で気軽に型変換できることだと思うので、 X::from(y) の形で使用することだけだと本当になんかちょっと短くなっただけでX::from_y(y) を実装するのと大差なくなってしまう 返信を追加
Takaaki Furuse2023/02/23(マジレスでなく、ジョークとして読んでください。) RobynというPython製のフレームワークがありまして・・・ これを使うという反則技もあります。 下ではPyO3というライブラリーを使ってAPIレベルからPythonコードをRustに変換してる感じです。 https://robyn.tech/ Takaaki Furuse2023/02/23下で動いてるのはRustなので、Rustで開発してる!(キリッ 返信を追加
フラワー2023/02/26突然の質問失礼致します。 気になったのですが、ユースケース層やドメイン層にDBコネクションオブジェクトを持たせる理由は何でしょうか? 基本的なDDDのプラクティスであれば、ドメインロジックとDBやFW等の詳細を分離するため、DBコネクションはインフラストラクチャ層で実装するものと理解しています。ユースケース層やドメイン層にDBコネクションを渡した場合、この分離ができなくなると思うのですが、もし何か理由があれば教えていただきたいです。(自分の理解が誤ってるかもしれないので、その場合は指摘いただけると幸いです。) Kyosuke Awata2023/02/27これは記事の内容が誤ってました、申し訳ありません。 正しくは「複数のリポジトリのコンストラクタにコネクションオブジェクトを渡す必要がある」でした。 ユースケースやドメインサービスは、生成されたリポジトリを受け取る形になりますね。 記事も併せて修正しました!ありがとうございます! 具体的なコードはこちらが分かりやすいかと思います。 フラワー2023/03/05いえいえ、回答ありがとうございます!そして、返信遅くなり申し訳ありません。 DBコネクションをリポジトリに渡すということであれば、自分のDDDの理解とずれてないので納得できました。 余談ですが、ソースコード読ませていただきました。なるほど、複数の機能でDBコネクションオブジェクトを使いまわしたいから、それぞれの機能のリポジトリにDBコネクションを渡さなければならず、複数のリポジトリのコンストラクタにコネクションオブジェクトを渡す必要があるということですね。 それで、DBコネクションは可変参照だから shared xor mutableで複数オブジェクトで共有できないと。 確かにGCありの言語なら、DB接続はインスタンス変数に持たせて使いまわしたりすることが常套手段だと思いますが、Rustだとその辺りが厄介になりそうなところかもですね。 僕も勉強になりました。ありがとうございました。 返信を追加
Kyosuke Awata2023/02/27これは記事の内容が誤ってました、申し訳ありません。 正しくは「複数のリポジトリのコンストラクタにコネクションオブジェクトを渡す必要がある」でした。 ユースケースやドメインサービスは、生成されたリポジトリを受け取る形になりますね。 記事も併せて修正しました!ありがとうございます! 具体的なコードはこちらが分かりやすいかと思います。
フラワー2023/03/05いえいえ、回答ありがとうございます!そして、返信遅くなり申し訳ありません。 DBコネクションをリポジトリに渡すということであれば、自分のDDDの理解とずれてないので納得できました。 余談ですが、ソースコード読ませていただきました。なるほど、複数の機能でDBコネクションオブジェクトを使いまわしたいから、それぞれの機能のリポジトリにDBコネクションを渡さなければならず、複数のリポジトリのコンストラクタにコネクションオブジェクトを渡す必要があるということですね。 それで、DBコネクションは可変参照だから shared xor mutableで複数オブジェクトで共有できないと。 確かにGCありの言語なら、DB接続はインスタンス変数に持たせて使いまわしたりすることが常套手段だと思いますが、Rustだとその辺りが厄介になりそうなところかもですね。 僕も勉強になりました。ありがとうございました。
Toshiki2025/03/06ご存知かもしれませんが、 JSONからRustの構造体を作りたいときはこのサイトを使うと楽になります! https://transform.tools/json-to-rust-serde 返信を追加
Discussion
(以下で言うORMはいわゆるActiveRecord的なものを指します)
ORMについて、多様性に欠けるという点ではエコシステムが充実していないというのはその通りだと思いますが、発展が遅く未熟というよりは、Rust開発者がORMを好まない傾向にあるためORM開発のモチベーションが比較的少ないというのもあるのではないかなと思っています。
明確なエビデンスがあるわけではないですが、Redditを観察しているとRustのORMやDB周りの話では毎回sqlx推しが集まっている印象があります。それもdieselが非同期処理をサポートしていないなどによる消去法的選択肢というよりは、ORMを使うことによる短期的メリットより、sqlxのような薄いながらもコンパイル時チェックなど本当に解決してほしい問題を解決してくれるものを推すようなコメントが多いように思います。
ただ、私もsqlx推しのためそのバイアスがかかっていますし、"dieselは使いたくないけど、かといってRustのエコシステムが未熟なことを認めたくない"だけのRust開発者もいるかもしれないので、上記の印象は眉唾ものですが、こういう視点もあるというコメントでした。
コメントありがとうございます!
Rust開発者の傾向まではチェックできていなかったので、大変参考になります!
chronoはserdeのSerialize/Deserialize実装を提供しています
Rustは言語機能としてfeature-gateを内蔵しており、ライブラリなどにおいても使う側が必要な機能だけを有効にすることができます.
serdeでのシリアライズ機能は、chronoの利用者のすべてが必要としているわけではないが、あると便利、みたいなものですよね.
なのでデフォルトでは無効にしつつ、featureを指定した場合に有効になる、のように実装されています.
Cargo.tomlで
などとすればSerialize/Deserialize出来るようになるはずです
ありがとうございます!
こちらのやり方の方が簡単なので、もっと簡単なやり方もあるよ!と追記しようと思います!
これは内部可変性といわれるパターンで解消できます.
記事内に書かれているRefCellも内部可変性を実現するための構造体のうちの一つです.
RcやRefCellはSendやSyncを実装していない(スレッドを跨ぐマルチスレッド環境では安全に扱えない)ので、Webサーバーなどマルチスレッドな環境では安全に扱えるArc<Mutex<T>>などが頻出します.
普通、データベースへのコネクションは処理のスレッドごとに別々に用意するものじゃないでしょうか。複数の処理で同時に同じコネクションを無理やり使い回すと、あるトランザクションの途中で別のトランザクションの処理が混じるみたいなカオスな状況になりそうな気がします。
コネクションを毎回何度も接続したり切断したりするのは非効率なのでコネクションプールを使って管理するのが一般的かと思いますがいかがでしょう。
コメントありがとうございます!
その通りだと思います!
そして今回もそのような作りになっています!
具体的には以下のような流れになります(詳細はサンプルコードを読んで頂けると分かりやすいかと思います)
scenarioでコネクションプールからコネクションオブジェクトを取得scenarioからpresentationに定義されている関数を呼び出すpresentationの関数では、ユースケースのインスタンスを生成する(必要ならリポジトリやドメインサービスを生成してユースケースに渡す。↑このリポジトリやドメインサービスを作る際に可変参照なコネクションオブジェクトが必要になります
返信ありがとうございます。そして本文のコードをうまく読み解けていなかったようですみません。
コネクションプールは既に使っておられて、コネクションプールからコネクションを取り出すまでは順調にできているのですね。それでいて
ということが必要だったということは、ユースケースやドメインサービスの中にコネクションの参照を保持しているということでしょうか。コネクションを保持するオブジェクトが複数あるのであれば一つのコネクションを共有するために Rc<RefCell> のパターンが必要になってくるのも腑に落ちます。
ただ自分ならオブジェクト内に保持するのではなくて必要になるたびに毎回関数の引数で渡す道を選びそうだなと思いました。その方がコンパイル時チェックに頼れる範囲が広いので。
Fromトレイトの話をするならIntoトレイトの話を入れてほしかった感。Fromトレイトの嬉しい点はFromトレイトを実装するとIntoトレイトが自動的に実装されて、into()で気軽に型変換できることだと思うので、X::from(y)の形で使用することだけだと本当になんかちょっと短くなっただけでX::from_y(y)を実装するのと大差なくなってしまう(マジレスでなく、ジョークとして読んでください。)
RobynというPython製のフレームワークがありまして・・・
これを使うという反則技もあります。
下ではPyO3というライブラリーを使ってAPIレベルからPythonコードをRustに変換してる感じです。
下で動いてるのはRustなので、Rustで開発してる!(キリッ
突然の質問失礼致します。
気になったのですが、ユースケース層やドメイン層にDBコネクションオブジェクトを持たせる理由は何でしょうか?
基本的なDDDのプラクティスであれば、ドメインロジックとDBやFW等の詳細を分離するため、DBコネクションはインフラストラクチャ層で実装するものと理解しています。ユースケース層やドメイン層にDBコネクションを渡した場合、この分離ができなくなると思うのですが、もし何か理由があれば教えていただきたいです。(自分の理解が誤ってるかもしれないので、その場合は指摘いただけると幸いです。)
これは記事の内容が誤ってました、申し訳ありません。
正しくは「複数のリポジトリのコンストラクタにコネクションオブジェクトを渡す必要がある」でした。
ユースケースやドメインサービスは、生成されたリポジトリを受け取る形になりますね。
記事も併せて修正しました!ありがとうございます!
具体的なコードはこちらが分かりやすいかと思います。
いえいえ、回答ありがとうございます!そして、返信遅くなり申し訳ありません。
DBコネクションをリポジトリに渡すということであれば、自分のDDDの理解とずれてないので納得できました。
余談ですが、ソースコード読ませていただきました。なるほど、複数の機能でDBコネクションオブジェクトを使いまわしたいから、それぞれの機能のリポジトリにDBコネクションを渡さなければならず、複数のリポジトリのコンストラクタにコネクションオブジェクトを渡す必要があるということですね。
それで、DBコネクションは可変参照だから shared xor mutableで複数オブジェクトで共有できないと。
確かにGCありの言語なら、DB接続はインスタンス変数に持たせて使いまわしたりすることが常套手段だと思いますが、Rustだとその辺りが厄介になりそうなところかもですね。
僕も勉強になりました。ありがとうございました。
ご存知かもしれませんが、
JSONからRustの構造体を作りたいときはこのサイトを使うと楽になります!