質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

Q&A

解決済

1回答

1156閲覧

actix-web で Data<dyn trait> を使い回す

requests.post

総合スコア10

Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

0グッド

0クリップ

投稿2022/09/09 11:18

実現したいこと

actix_web::web::Data を使って本番環境ではDBのコネクションを持ったプールをinnerに持ったClient, テスト時はインメモリで定義した構造体を返すClientをAppに渡したいです。UserRepository という trait を定義して、空のstructであるUserClientをimplさせればweb::Dataで包めると思ったのですがうまくいきません。そもそものDataで渡す筋が悪いというのもあるのしょうか?

該当のソースコード

rust

1use actix_web::{ 2 get, 3 web::{self, Data}, 4 App, HttpServer, Responder, 5}; 6use serde::Serialize; 7 8#[derive(Serialize, Debug)] 9pub struct User { 10 pub name: String, 11} 12 13impl User { 14 pub fn new(name: String) -> Self { 15 Self { name } 16 } 17} 18 19pub trait UserRepository { 20 fn get_users(&self) -> Vec<User>; 21} 22 23// インメモリのテスト用クライアント 24#[derive(Debug, Clone)] 25pub struct UserClient; 26 27impl UserRepository for UserClient { 28 fn get_users(&self) -> Vec<User> { 29 vec![User::new("hoge".to_string()), User::new("fuga".to_string())] 30 } 31} 32 33#[get("/users")] 34pub async fn get_users(cl: web::Data<dyn UserRepository>) -> impl Responder { 35 let users = cl.into_inner().get_users(); 36 web::Json(users) 37} 38 39#[actix_rt::main] 40async fn main() -> std::io::Result<()> { 41 std::env::set_var("RUST_LOG", "debug"); 42 std::env::set_var("RUST_BACKTRACE", "1"); 43 env_logger::init(); 44 45 let client: Data<dyn UserRepository> = Data::new(UserClient); 46 47 HttpServer::new(move || { 48 App::new() 49 .app_data(Data::new(client.clone())) 50 .service(get_users) 51 }) 52 .bind("0.0.0.0:9090")? 53 .run() 54 .await 55}

発生している問題・エラーメッセージ

console

1error[E0308]: mismatched types 2 --> examples/user.rs:44:44 3 | 444 | let client: Data<dyn UserRepository> = Data::new(UserClient); 5 | ------------------------ ^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn UserRepository`, found struct `UserClient` 6 | | 7 | expected due to this 8 | 9 = note: expected struct `Data<dyn UserRepository>` 10 found struct `Data<UserClient>` 11 12For more information about this error, try `rustc --explain E0308`. 13error: could not compile `actix-web-sample` due to previous error

試したこと

rust

1pub async fn get_users<T: UserRepository>(cl: web::Data<T>) -> impl Responder { 2 let users = cl.into_inner().get_users(); 3 web::Json(users) 4}

といった風にコントローラ側をジェネリクスにしたが、これだとUserClientとして解決できないので違うということは分かります。また、

rust

1 HttpServer::new(move || { 2 App::new() 3 .app_data(Data::new(UserClient)) 4 .service(get_users) 5 })

という風に client を定義せずに直接渡す例も試しました。コンパイル自体は通るのですが、エンドポイントが500を返してうまくいきませんでした。

console

1➜ actix-web git:(master) ✗ http localhost:9090/users 2HTTP/1.1 500 Internal Server Error 3content-length: 96 4content-type: text/plain; charset=utf-8 5date: Fri, 09 Sep 2022 11:13:04 GMT 6 7Requested application data is not configured correctly. View/enable debug logs for more details.

補足情報(FW/ツールのバージョンなど)

toml

1[package] 2name = "actix-web-sample" 3version = "0.1.0" 4edition = "2021" 5 6# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 8[dependencies] 9actix-web = "4.0.1" 10actix-rt = "2.7.0" 11serde = { version = "1.0.136", features = ["derive"] } 12env_logger = "0.9.0" 13log = "0.4.17"

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

dyn Trait を使うときはコンパイル時にサイズが分からないので何かしらの参照の形で渡してあげる必要があります。今回の例だと複数スレッドで共有するので std::sync にある Arc が適当なようです。また、スレッド間で共有するには Arc の中身が SendSync も実装している必要があるので、合せて以下のような書き方をすればコンパイルが通ります。

rust

1let client: Data<Arc<dyn UserRepository + Send + Sync>> = Data::new(Arc::new(UserClient));

(get_users の方も同様に書き換えます)

因みにArcを使うという話はactix-webのドキュメントにも載っています。

Data in actix_web::web - Rust

また、 dyn Trait を使わずに直接 UserClient を使うのも試されたようですが、それも正しいやり方です。やりたいことに合わせて好きな方を選ぶとよいのですが、恐らく元の意図としては UserRepository で抽象化したクライアントを作りたいのではないかと思うので dyn Trait を使う方がよいでしょう。

さて、 Data<UserClient> と書くと実行時エラーになったとのことですが、これは app_data のところのミスですね。

rust

1let client: Data<UserClient> = Data::new(UserClient); 2// … 3 .app_data(Data::new(client.clone()))

よく見ると Data に2回包んでいます。 app_data に渡すところで Data::new をやめて .app_data(client.clone()) としてあげればエラーにならずに動作しました。

console

1$ curl -v localhost:9090/users 2* Trying 127.0.0.1:9090... 3* Connected to localhost (127.0.0.1) port 9090 (#0) 4> GET /users HTTP/1.1 5> Host: localhost:9090 6> User-Agent: curl/7.81.0 7> Accept: */* 8> 9* Mark bundle as not supporting multiuse 10< HTTP/1.1 200 OK 11< content-length: 33 12< content-type: application/json 13< date: Mon, 12 Sep 2022 16:10:15 GMT 14< 15* Connection #0 to host localhost left intact 16[{"name":"hoge"},{"name":"fuga"}]

投稿2022/09/12 16:11

blackenedgold

総合スコア468

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

requests.post

2022/09/29 11:52

レスポンス遅れて申し訳ございません、意図もご推察通りです、回答ありがとうございました!!!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問