如何用期货引入特征间接?

How to introduce trait indirection with futures?

我有一个 tokio tcp 服务器,它应该将单独的传入连接交给服务。我如何正确处理间接寻址以便服务器可以使用不同的服务实现?我已求助于在服务内部调用 tokio::spawn(),因为我找不到 return 间接访问未来的方法。

这是我正在做的一个最简单的例子:

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 impl 都必须调用 tokio::spawn() 而不仅仅是 return 执行任务。我也更喜欢服务器是否处理生成,因为它可能需要处理优先级和排队。也很难测试不 return 任何东西的方法。

这里是 the other things I've been trying 的游乐场 link。 要查看完整上下文,请转到 Samotop source 并接受 fn。

如果 trait 方法实现可以 return 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
        ))
    }
}

futures 或 Tokio 没有什么特别之处,这只是 Rust。我强烈建议您在深入异步编程的复杂世界之前学习如何使用基本的 Rust 功能。从 The Rust Programming Language, specicially the section on trait objects:

开始
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))
}

这被明确称为 the Tokio documentation on how to return a Future 的第一个建议。

if trait method implementation could return impl Trait!

据我所知,这是不可能的。 returns 和 impl Trait returns 可能不同大小的具体类型的每个函数。特定的调用者不知道要为任意特征实现分配多少堆栈 space。

另请参阅:

  • What is the correct way to return an Iterator (or any other trait)?
  • Sending trait objects between threads in Rust

从 trait 实现中返回 impl Trait 是不行的,因为我必须(再次?)学习我们需要一个具体的大小。 Box整理一下可能会奏效,但我一直在思考。所以我稍微扭转了局面,将 Service return 改为 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);
}