Здравствуйте!
Продолжаю осваивать Rust тренируясь на разработке клиента Google OAuth 2.0. Никак в голове не укладываются трёхэтажные шаблоны.
Есть вот такой код
fn retrieve_auth_url() {
let (sender, receiver) = futures::sync::oneshot::channel::<Url>();
hyper::rt::run(hyper::rt::lazy(move || {
retrieve_url(sender)
}));
let location = receiver.wait();
println!("Response: {:?}", location);
}
fn retrieve_url(send: Sender<Url>) -> Client<HttpsConnector<Error>> {
let uri = Url::parse_with_params(GOOGLE_AUTH_URL, [
("client_id", CLIENT_ID),
("redirect_uri", "http://[::1]:3000"),
("response_type", "code"),
("scope", "https://www.googleapis.com/auth/drive")
].iter());
let url = uri.unwrap().as_str().parse::<Uri>().unwrap();
let https = HttpsConnector::new(1).unwrap();
let client = Client::builder().build::<_, hyper::Body>(https);
client.get(url)
.map(|res| {
Url::parse(res.headers().get("location").unwrap().to_str().unwrap()).unwrap()
})
.map_err(|err| {
err
})
}
Каким должен быть тип, возвращаемый функцией retrieve_url? Компилятор на него постоянно ругается, аргументируя тем, что hyper::rt::lazy требует другой тип (я пробовал разные варианты). При этом, если перенести код функции в замыкание - всё работает.
Ощущение, что я что-то упустил с шаблонами.
Заранее спасибо.
PS. А почему тип MapErr, который возвращает функция Client::map_err может выступать в качестве типа Client? Вот пример https://hyper.rs/guides/client/basic/
По коду.
Здесь много чего не совсем понятно/завершено:
Вы создаете канал:
let (sender, receiver) = futures::sync::oneshot::channel::<Url>();
передаете sender:
retrieve_url(sender)
потом ждете ответа:
let location = receiver.wait();
Но внутри функции retrieve_url ничего не отправляете!
2. client.get(url) возвращает футуру (да, вы делаете мапы, но в итоге это все же футура) . Таким образом возвращаемое значение должно быть impl Future:
hyper::rt::run(<impl Future>) ждет на вход что-либо имплементирующее треит Future (в данном случае hyper::rt::lazy излишен) та что код будет примерно таким:
Future это trait, а трейты в расте это что-то на подобии интерфейсов в других языках, это не конкретные типы, а значит у них нет заранее известного(во время компиляции) размера, и их нельзя просто так возвращать (или передавать) из функцию. Запись типа impl Future означат лишь что мы знаем что функция возвращает объект который имплементирует трейт Future. В данном случае конкретный тип объекта буде MapErr<Map<Get …>>> его нельзя записать (как тип возвращаемого значения), т.к. он содержит дженерики с замыканиями, поэтому мы пользуемся impl Future
Но исполняться-то в недрах Tokio должен именно Client, а не MapErr!
Не совсем. Токио ничего о Clientне знает. Токио нужет любой объект (будь то MapErr или MySuperPuperFuture) который имеет имплементацию трейта Future. Вот пример того как может выглядеть метод run у токио:
как видим никакого Client тут и впомине нет (Client это объект Hyper для установления связи с удаленным сервером и передачи данных. Client даже не имплементит trait Future, у него есть метод .get который возвращает что-то что имплементирует Future)