Как ввести черты косвенности с фьючерсами? - PullRequest
0 голосов
/ 06 июля 2018

У меня есть tkp-сервер Tokio, который должен передавать отдельные входящие соединения в службу. Как правильно обработать косвенное обращение, чтобы сервер мог работать с различными реализациями служб? Я прибег к звонку tokio::spawn() внутри службы, потому что не смог найти способ вернуть будущее с косвенным указанием.

Вот минимальный пример того, что я делаю:

extern crate tokio;

use tokio::prelude::future::FutureResult;
use tokio::prelude::*;

struct Subject {
    name: String,
}    
struct MySvc {
    name: String,
}    
trait Svc6 {
    fn handle6(&self, subject: Subject);
}
impl Svc6 for MySvc {
    fn handle6(&self, subject: Subject) {
        let task = future::ok((self.name.to_string(), subject))
            .and_then(|(n, s)| Ok(println!("#6. Hi {}! My name is {}.", s.name, n)));
        tokio::spawn(task);
    }
}   

#[test]
fn svc6_works() {
    let svc = MySvc {
        name: "Zorg".into(),
    };
    let subj = Subject {
        name: "Gandalf".into(),
    };
    tokio::run(future::ok(svc).and_then(|s| Ok(s.handle6(subj))));
}

Хотя это работает с косвенным обращением, меня беспокоит, правильно ли я использую tokio. Каждый Svc6 импл должен вызывать tokio::spawn(), а не просто возвращать задачу. Я также предпочел бы, чтобы сервер обрабатывал нерест, поскольку ему, возможно, придется иметь дело с расстановкой приоритетов и очередями. Также сложно протестировать метод, который ничего не возвращает.

Вот ссылка на игровую площадку других вещей, которые я пробовал . Чтобы увидеть полный контекст, перейдите на Самотопский источник и нажмите «Принять».

Было бы замечательно, если бы реализация метода trait могла возвращать impl Trait!

trait Svc1 {
    fn handle1(&self, subject: Subject) -> Future<Item = (), Error = ()>;
}
impl Svc1 for MySvc {
    // error[E0562]: `impl Trait` not allowed outside of function and inherent method return types
    fn handle1(&self, subject: Subject) -> impl Future<Item = (), Error = ()> {
        future::ok(println!(
            "#1. Hi {}! My name is {}.",
            subject.name, self.name
        ))
    }
}

Ответы [ 2 ]

0 голосов
/ 06 июля 2018

Возвращение impl Trait из реализаций черт - некуда, так как мне пришлось (опять?) Узнать, что нам нужен конкретный размер. Box все может работать, но я продолжаю размышлять. Поэтому я немного изменил ситуацию и заставил Service вернуть Sink вместо этого, чтобы получить предмет. Затем я направляю поток в раковину. Кажется, это можно обернуть с помощью tokio::spawn():

use futures::StartSend;
use tokio;
use tokio::io;
use tokio::prelude::*;

struct Subject {
    name: String,
}

trait Svc {
    type Receiver;
    type Error;
    fn start(&self) -> Self::Receiver;
}

struct MySvc {
    name: String,
}

impl Svc for MySvc {
    type Receiver = MyReceiver;
    type Error = io::Error;
    fn start(&self) -> Self::Receiver {
        MyReceiver::new(&self.name)
    }
}

struct MyReceiver {
    name: String,
    pending: Box<Future<Item = (), Error = ()> + Send>,
}

impl MyReceiver {
    fn say_hi(&self, subject: Subject) {
        println!("Hi {}! It's {}.", subject.name, self.name)
    }
    fn new(name: impl ToString) -> Self {
        Self {
            name: name.to_string(),
            pending: Box::new(future::ok(())),
        }
    }
}

impl Future for MyReceiver {
    type Item = Self;
    type Error = Self;
    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        Ok(Async::Ready(MyReceiver::new(&self.name)))
    }
}

impl Sink for MyReceiver {
    type SinkItem = Subject;
    type SinkError = ();
    fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
        self.say_hi(item);
        Ok(AsyncSink::Ready)
    }
    fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
        Ok(Async::Ready(()))
    }
}

#[test]
fn try() {
    let svc = MySvc { name: "jOy".into() };

    let task = future::ok(svc)
        .and_then(|s| {
            s.start().and_then(|r| {
                let subject = Subject {
                    name: "Miou".into(),
                };
                let task = stream::once(Ok::<Subject, ()>(subject))
                    .forward(r)
                    .map_err(|_| ())
                    .and_then(|_| Ok(()));
                tokio::spawn(task);
                Ok(())
            })
        })
        .and_then(|_| Ok(()))
        .map_err(|_| ());

    tokio::run(task);
}
0 голосов
/ 06 июля 2018

Здесь нет ничего особенного в фьючерсах или Tokio, это просто Rust. Я настоятельно рекомендую вам научиться использовать базовые функциональные возможности Rust, прежде чем погрузиться в сложный мир асинхронного программирования. Начните с Язык программирования Rust , в частности раздел объектов признаков :

trait Svc {
    fn handle(&self, subject: Subject) -> Box<Future<Item = (), Error = ()> + Send>;
}

impl Svc for MySvc {
    fn handle(&self, subject: Subject) -> Box<Future<Item = (), Error = ()> + Send> {
        Box::new(future::ok(println!(
            "#1. Hi {}! My name is {}.",
            subject.name, self.name
        )))
    }
}

#[test]
fn svc_works() {
    let svc = MySvc {
        name: "Zorg".into(),
    };
    let subj = Subject {
        name: "Gandalf".into(),
    };
    tokio::run(svc.handle(subj))
}

Это явно называется предложением номер один для документации Tokio о том, как вернуть Future.

если реализация метода trait может вернуть impl Trait!

Насколько я знаю, это невозможно. Каждая функция, которая возвращает impl Trait, возвращает конкретный тип потенциально другого размера. Определенный вызывающий не знает, сколько стекового пространства выделить для реализации произвольной характеристики.

Смотри также:

...