Как передать структуру с параметрами типа в качестве аргумента функции?
Как мне передать экземпляр 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);
}
Я все еще не совсем уверен, почему это работает или требуется, но я надеюсь, что этот ответ поможет кому-то еще.