Как передать структуру с параметрами типа в качестве аргумента функции?

Как мне передать экземпляр EcsClient с подписью impl<P, D> EcsClient<P, D> where P: ProvideAwsCredentials, D: DispatchSignedRequest к функции в качестве ссылки в Rust? Моя попытка такова:

extern crate rusoto;

use std::default::Default;

use rusoto::{ DefaultCredentialsProvider, Region };
use rusoto::ecs::{ EcsClient };
use rusoto::default_tls_client;

fn get_task_definition_revisions(client: &EcsClient) {
    // Use EscClient instance here
}

fn main() {
    let provider = DefaultCredentialsProvider::new().unwrap();
    let client = EcsClient::new(default_tls_client().unwrap(), provider, Region::EuWest1).unwrap();

    get_task_definition_revisions(&client);

}

Это дает мне следующую ошибку:

error[E0243]: wrong number of type arguments: expected 2, found 0
 --> src/main.rs:9:43
  |
9 | fn get_task_definition_revisions(client: &EcsClient) {
  |                                           ^^^^^^^^^ expected 2 type arguments

Моя попытка исправить это так:

extern crate rusoto;

use std::default::Default;

use rusoto::{
    DefaultCredentialsProvider,
    Region,
    ProvideAwsCredentials,
    DispatchSignedRequest
};
use rusoto::ecs::{ EcsClient, ListTaskDefinitionsRequest };
use rusoto::default_tls_client;

fn get_task_definition_revisions(client: &EcsClient<ProvideAwsCredentials, DispatchSignedRequest>) {
    // Use EcsClient instance here
}

fn main() {
    let provider = DefaultCredentialsProvider::new().unwrap();
    let client = EcsClient::new(default_tls_client().unwrap(), provider, Region::EuWest1);

    get_task_definition_revisions(&client);
}

Что дает мне:

error[E0277]: the trait bound `rusoto::ProvideAwsCredentials + 'static: std::marker::Sized` is not satisfied
  --> src/main.rs:14:1
   |
14 |   fn get_task_definition_revisions(client: &EcsClient<P, D>) {
   |  _^ starting here...
15 | |  let defs = client.list_task_definitions(&ListTaskDefinitionsRequest {
16 | |      family_prefix: None,
17 | |      max_results: None,
18 | |      next_token: None,
19 | |      sort: None,
20 | |      status: Some("ACTIVE".to_string()),
21 | |  });
22 | | }
   | |_^ ...ending here: the trait `std::marker::Sized` is not implemented for `rusoto::ProvideAwsCredentials + 'static`
   |
   = note: `rusoto::ProvideAwsCredentials + 'static` does not have a constant size known at compile-time
   = note: required by `rusoto::ecs::EcsClient`

error[E0277]: the trait bound `rusoto::DispatchSignedRequest + 'static: std::marker::Sized` is not satisfied
  --> src/main.rs:14:1
   |
14 |   fn get_task_definition_revisions(client: &EcsClient<P, D>) {
   |  _^ starting here...
15 | |  let defs = client.list_task_definitions(&ListTaskDefinitionsRequest {
16 | |      family_prefix: None,
17 | |      max_results: None,
18 | |      next_token: None,
19 | |      sort: None,
20 | |      status: Some("ACTIVE".to_string()),
21 | |  });
22 | | }
   | |_^ ...ending here: the trait `std::marker::Sized` is not implemented for `rusoto::DispatchSignedRequest + 'static`
   |
   = note: `rusoto::DispatchSignedRequest + 'static` does not have a constant size known at compile-time
   = note: required by `rusoto::ecs::EcsClient`

Это похоже на кроличью нору, я не должен был спускаться.

Я также попытался изменить сигнатуру функции, чтобы принять дженерики, однако EcsClient это структура, а не черта. Поиск в Google не помогает, потому что я не знаю правильных терминов для поиска.

Этот вопрос, кажется, подразумевает, что я должен быть в состоянии объявить такую ​​функцию, как fn my_func(client: &EcsClient) { ... } и это будет работать, так почему же не приведенный выше пример?

2 ответа

Решение

Проблема в том, что EcsClient это не тип, это план создания типа (также известный как "конструктор типов").

В результате вы не можете использовать только EcsClient когда требуется тип, будь то в функциях или для членов структуры; вместо этого каждый раз вы должны использовать его для создания типа, указав его общие параметры.

Таким образом, первым шагом является введение параметров типа:

fn get_task_definition_revisions<P, D>(client: &EcsClient<P, D>) {}

Теперь, однако, компилятор будет жаловаться, что P а также D недостаточно стеснены: EcsClient принимать только очень специфический вид P а также D!

Следующим шагом, таким образом, является поиск границ, указанных для P а также D в определении EcsClient и применять их. Это просто скопировать / вставить на этом этапе:

fn get_task_definition_revisions<P, D>(client: &EcsClient<P, D>)
    where P: ProvideAwsCredentials,
          D: DispatchSignedRequest
{
}

И тогда ты золотой.

Если вам нужно больше возможностей P или же D для этой конкретной функции, не стесняйтесь ограничить их адекватно, добавив больше границ, используя +:

fn get_task_definition_revisions<P, D>(client: &EcsClient<P, D>)
    where P: ProvideAwsCredentials + 'static,
          D: DispatchSignedRequest
{
}

Если вам интересно, почему Руст решил, что вы должны повторить границы для P а также D когда он может их совершенно точно понять, это потому, что он заботится о вас. Точнее, он заботится о вас через 6 месяцев и о следующем сопровождающем. Итак, принимая позицию, которую вы пишете один раз и читаете много, это заставляет вас копировать границы, чтобы потом вам не приходилось задумываться о том, что они из себя представляют, и рекурсивно анализировать каждый тип / функцию, используемый для болезненной агрегации всех фрагментов., В Rust, в следующий раз, когда вы прочитаете функцию, вы получите всю информацию прямо здесь.

Я неправильно понял синтаксис. Кажется, что P а также D заполнители, как T было бы. Мне нужно указать, что это за типы, поэтому подпись теперь выглядит так:

fn get_task_definition_revisions<P: ProvideAwsCredentials, D: DispatchSignedRequest>(client: &EcsClient<P, D>) {
    ...
}

Я не пользуюсь P или же D в теле функции, но они должны быть объявлены.

Полный пример теперь выглядит так:

extern crate rusoto;

use std::default::Default;

use rusoto::{
    DefaultCredentialsProvider,
    Region,
    ProvideAwsCredentials,
    DispatchSignedRequest
};
use rusoto::ecs::{ StringList, EcsClient, ListTaskDefinitionsRequest };
use rusoto::default_tls_client;

fn get_task_definition_revisions<P: ProvideAwsCredentials, D: DispatchSignedRequest>(client: &EcsClient<P, D>) {
    let res = client.list_task_definitions(&ListTaskDefinitionsRequest {
        family_prefix: None,
        max_results: None,
        next_token: None,
        sort: None,
        status: Some("ACTIVE".to_string()),
    });

    // ...
}

fn main() {
    let provider = DefaultCredentialsProvider::new().unwrap();
    let client = EcsClient::new(default_tls_client().unwrap(), provider, Region::EuWest1);

    get_task_definition_revisions(&client);
}

Я все еще не совсем уверен, почему это работает или требуется, но я надеюсь, что этот ответ поможет кому-то еще.

Другие вопросы по тегам