2017-11-13 8 views
0

私は次のコードのようなものをコンパイルしようとしています。私がすべての武器をfutures::future::IntoFutureとしたいと思っていることを理解するのを助ける必要があるようです。それは外側のand_thenがコールバック/クロージャー/デリゲートから期待しているものですから。IntoFutureに列挙型をラップしますか?

今のところ、すべてのアームは最も簡単な列挙型のNothingUseful()でスタブされていますが、最終的には返されたHTTPステータスコードおよび/または本文の内容に応じてさまざまなアクションをとることになります。

extern crate futures; 
extern crate hyper; 
extern crate tokio_core; 

use futures::{future, Future, Stream}; 
use hyper::{Client, Error as HyperError, Response, StatusCode, Uri}; 
use tokio_core::reactor::Core; 

struct RecurseUrl { 
    uri: Uri, 
    remaining_attempts: u8, 
} 

enum FetchResult { 
    SimpleData(u16), 
    RecurseUrls(Vec<RecurseUrl>), 
    NothingUseful(), 
} 

fn handle_redirect(res: &Response) -> future::FutureResult<FetchResult, HyperError> { 
    future::ok(FetchResult::NothingUseful()) 
} 

fn main() { 
    let url = "http://someurl.com" 
     .parse() 
     .expect("Unable to parse URL"); 

    let mut core = Core::new().expect("Unable to instantiate Tokio Core"); 
    let client = Client::new(&core.handle()); 

    let work = client.get(url).and_then(|res| { 

     match res.status() { 
      StatusCode::TemporaryRedirect => handle_redirect(&res), 
      StatusCode::PermanentRedirect => handle_redirect(&res), 
      StatusCode::Ok => { 
       res.body().concat2().and_then(move |body| { 
        Ok(FetchResult::NothingUseful()) 
       }) 
      }, 
      _ => { 
       Ok(FetchResult::NothingUseful()) 
      } 
     } 
    }); 

    core.run(work).expect("Problem running work"); 
} 
error[E0308]: match arms have incompatible types 
    --> main.rs:34:13 
    | 
34 |/   match res.status() { 
35 | |     StatusCode::TemporaryRedirect => handle_redirect(&res), 
36 | |     StatusCode::PermanentRedirect => handle_redirect(&res), 
37 | |     StatusCode::Ok => { 
... | 
44 | |     } 
45 | |    } 
    | |_____________^ expected struct `futures::FutureResult`, found struct `futures::AndThen` 
    | 
    = note: expected type `futures::FutureResult<FetchResult, hyper::Error>` 
       found type `futures::AndThen<futures::stream::Concat2<hyper::Body>, std::result::Result<FetchResult, hyper::Error>, [[email protected]:38:51: 40:22]>` 
note: match arm with an incompatible type 
    --> main.rs:37:35 
    | 
37 |     StatusCode::Ok => { 
    | ___________________________________^ 
38 | |      res.body().concat2().and_then(move |body| { 
39 | |       Ok(FetchResult::NothingUseful()) 
40 | |      }) 
41 | |     }, 
    | |_________________^ 
+0

IntoFutureは形質であり、安定したRustの形質に何かを作るだけの方法はないのですか? – Shepmaster

+0

@Shepmaster私はそれが特性だと理解しています。私はいくつかの明示的なタイプのヒントやキャストや何かを追加できることを期待しています。 future :: ok()を使ってそれぞれの腕を実際の未来に戻すのは簡単ですが、結果のimplsのいくつかがAndThenでFutureResultがいくつかあり、それらを変数に代入しようとすると問題に遭遇します彼らはすべて共通しているはずの特性でタイプされている、それはSizedのためにマークされていないことについて不平を言う。たぶん私は全く別のアプローチをとるべきでしょうか? – steamer25

答えて

1

私は、それが外and_thenがコールバック/クロージャ/デリゲートから期待しているものですので、試合アームのすべてが、futures::future::IntoFutureとみなすことにしたいです。

and_thenはクロージャの戻り型は、が形質IntoFutureを実装単一コンクリート型あることと予想しています。 matchは、複数の具体的な型を返します。これは、コンパイラが割り当てるスタック領域を知らないため、Rustでは使用できません。

すべてのさまざまなタイプを1つの統一タイプに変換する必要があります。最も簡単なは、形質オブジェクト(Box<Future<Item = FetchResult, Error = hyper::Error>>)を作成、それらすべてをボックスにある:

let work = client.get(url).and_then(|res| -> Box<Future<Item = FetchResult, Error = hyper::Error>> { 
     match res.status() { 
      StatusCode::TemporaryRedirect => Box::new(handle_redirect(&res)), 
      StatusCode::PermanentRedirect => Box::new(handle_redirect(&res)), 
      StatusCode::Ok => Box::new(
       res.body() 
        .concat2() 
        .map(move |body| FetchResult::NothingUseful()), 
      ), 
      _ => Box::new(future::ok(FetchResult::NothingUseful())), 
     } 
    }, 
); 

また、独自のタイプを作成し、そのためのFutureを実装することができます。あなたはこのようなもので応答の遺体のうちURIを読んで扱うことができる

#[macro_use] 
extern crate futures; 
extern crate hyper; 
extern crate tokio_core; 

use futures::{Async, Future, Poll}; 
use hyper::client::{FutureResponse, HttpConnector}; 
use hyper::{Client, Response, StatusCode, Uri}; 
use tokio_core::reactor::Core; 

struct RecurseUrl { 
    client: Client<HttpConnector>, 
    future: FutureResponse, 
    remaining_attempts: u8, 
} 

impl RecurseUrl { 
    fn new(client: Client<HttpConnector>, uri: Uri) -> Self { 
     let future = client.get(uri); 
     Self { 
      client, 
      future, 
      remaining_attempts: 3, 
     } 
    } 
} 

impl Future for RecurseUrl { 
    type Item = hyper::Response; 
    type Error = hyper::Error; 

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> { 
     let response = try_ready!(self.future.poll()); 

     match response.status() { 
      StatusCode::TemporaryRedirect | StatusCode::PermanentRedirect => { 
       if self.remaining_attempts == 0 { 
        panic!("return a real error") 
       } 

       let next_uri = get_redirect_uri_from_response(&response); 
       let next_future = self.client.get(next_uri); 
       self.future = next_future; 
       self.remaining_attempts -= 1; 

       Ok(Async::NotReady) 
      } 
      StatusCode::Ok => Ok(Async::Ready(response)), 
      _ => panic!("return a real error"), 
     } 
    } 
} 

fn get_redirect_uri_from_response(_response: &Response) -> Uri { 
    unimplemented!() 
} 

fn main() { 
    let uri = "http://someurl.com".parse().expect("Unable to parse URL"); 

    let mut core = Core::new().expect("Unable to instantiate Tokio Core"); 
    let client = Client::new(&core.handle()); 

    let work = RecurseUrl::new(client, uri); 
    core.run(work).expect("Problem running work"); 
} 

use futures::stream::{Stream, FuturesUnordered, Concat2}; 

struct WebCrawler { 
    client: Client<HttpConnector>, 
    to_fetch: FuturesUnordered<FutureResponse>, 
    fetching: FuturesUnordered<Concat2<hyper::Body>>, 
} 

impl WebCrawler { 
    fn new(client: Client<HttpConnector>, uri: Uri) -> Self { 
     let future = client.get(uri); 
     let to_fetch: FuturesUnordered<_> = Some(future).into_iter().collect(); 

     Self { 
      client, 
      to_fetch, 
      fetching: FuturesUnordered::new(), 
     } 
    } 
} 

impl Stream for WebCrawler { 
    type Item = hyper::Chunk; 
    type Error = hyper::Error; 

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { 
     loop { 
      match self.to_fetch.poll()? { 
       Async::Ready(Some(s)) => { 
        self.fetching.push(s.body().concat2()) 
       }, 
       Async::Ready(None) | Async::NotReady => break, 
      } 
     } 

     loop { 
      match self.fetching.poll()? { 
       Async::Ready(Some(body)) => { 
        for uri in get_uris_from_body(&body) { 
         self.to_fetch.push(self.client.get(uri)); 
        } 
        return Ok(Async::Ready(Some(body))); 
       }, 
       Async::Ready(None) | Async::NotReady => break, 
      } 
     } 

     if self.to_fetch.is_empty() && self.fetching.is_empty() { 
      Ok(Async::Ready(None)) 
     } else { 
      Ok(Async::NotReady) 
     } 
    } 
} 

fn get_uris_from_body(_body: &hyper::Chunk) -> Vec<Uri> { 
    unimplemented!() 
} 

この実装はBufferUnorderedのことからcribbedた。これは、あなたが任意の割り当てを避けることができます。クロールの深さを渡して管理するには、それを強化する必要がありますが、これは可能なことの良いスケッチだと思います。

+0

"各バリアントの列挙型を作成し、各インプリメンテーションに委譲して将来を実装する"ということを明確にしてください。 – steamer25

+0

@ steamer25私はenumで少し演奏しましたが、本当に好きではありませんでしたので、私は別の方法を提案します。私はあなたのオリジナルが 'RecurseUrls(Vec )'を持っているのを見ています - 単一のURIを取ってから複数の*を検索する必要がありますか? – Shepmaster

+0

まだ更新された答えを見ていない(大きな感謝!)しかし、私の考えはリダイレクトと同じロジックを使用して後続のページへのリンクを処理することです。すなわち、HTTPレスポンスは要求されたページのリダイレクトを送信するか、または新しいページを追いかけるために一連のhrefでHTMLを送信するかもしれません。いずれの場合も、再帰の深さは制限されるべきです。 – steamer25

関連する問題