简体   繁体   English

在线程中使用特征方法

[英]Using trait methods in threads

Basically, I'm making a program that's listening to a bunch of ports and that handles incoming packets in different ways. 基本上,我正在编写一个侦听一堆端口并以不同方式处理传入数据包的程序。 I decide to bundle this code into a Trait: 我决定将此代码捆绑为一个Trait:

use std::old_io::{TcpStream, TcpListener, Listener, Acceptor, EndOfFile, IoResult};
use std::thread::Thread;

trait Server {
    fn new(port: u16) -> Self;

    fn hostname(&self) -> &String;

    fn initialize(&self) {
        let acceptor = TcpListener::bind(self.hostname().as_slice()).listen().unwrap();
        Thread::spawn(move|| {
            let mut acceptor = acceptor;
            for incoming_stream in acceptor.incoming() {
                match incoming_stream {
                    Ok(stream) => {
                        self.handle_client(stream);
                    },
                    Err(ref e) if e.kind == EndOfFile => break,
                    Err(e) => panic!("Unexpected error: {}", e),
                }
            }
        });
    }

    fn handle_client(&self, stream: TcpStream) -> ();
}

pub struct InternodeServer {
    hostname: String,
}

impl Server for InternodeServer {
    fn new(port: u16) -> InternodeServer {
        let hostname = format!("127.0.0.1:{}", port);
        InternodeServer {
            hostname: hostname,
        }
    }

    fn hostname(&self) -> &String {
        &self.hostname
    }

    fn handle_client(&self, stream: TcpStream) {
        println!("Received connection");
        let mut stream = stream;
        let response = b"Hello\r\n";
        let _ = stream.write_all(response);
        let _ = stream.close_write();
    }
}

fn main() {
    let test_server = <InternodeServer as Server>::new(9337);
    test_server.initialize();
}

However, this code won't work because you can't send Self . 但是,此代码将无法工作,因为您无法发送Self This is the error I receive: 这是我收到的错误:

test.rs:11:9: 11:22 error: the trait `core::marker::Send` is not implemented for the type `Self` [E0277]
test.rs:11         Thread::spawn(move|| {
                   ^~~~~~~~~~~~~
test.rs:11:9: 11:22 note: `Self` cannot be sent between threads safely
test.rs:11         Thread::spawn(move|| {
                   ^~~~~~~~~~~~~

So I also tried making handle_client a static method to avoid self. 因此,我还尝试将handle_client设为静态方法以避免self。 To do this, I simply changed handle_client to: 为此,我只是将handle_client更改为:

fn handle_client(stream: TcpStream)

And referenced it by doing: 并通过以下方式引用了它:

Server::handle_client(stream);

However, I can't reference InternodeServer's static methods from Server's initialize method. 但是,我无法从Server的initialize方法中引用InternodeServer的静态方法。 When compiling, I get an error like: 编译时,出现如下错误:

test.rs:16:25: 16:46 error: type annotations required: cannot resolve `_ : Server` [E0283]
test.rs:16                         Server::handle_client(stream);
                                   ^~~~~~~~~~~~~~~~~~~~~
test.rs:16:25: 16:46 note: required by `Server::handle_client`
test.rs:16                         Server::handle_client(stream);

Is there any way around this? 有没有办法解决?

I don't think that rust will allow you to invoke object methods directly from other thread because "move" closures cannot borrow anything, only move. 我认为rust不会允许您直接从其他线程调用对象方法,因为“移动”闭包不能借用任何东西,只能移动。

So you have to use some kind of inter-thread communication tool, for example, channels: 因此,您必须使用某种线程间通信工具,例如通道:

use std::thread::Thread;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{channel, Sender, Receiver, RecvError};
use std::net::{TcpStream, TcpListener};
use std::io::{ErrorKind, Write};

trait Server {
    fn new(port: u16) -> Self;

    fn hostname(&self) -> &String;

    fn initialize(&mut self, _detached: bool) {
        let acceptor = TcpListener::bind(self.hostname().as_slice()).unwrap();
        let server_tx = self.make_pipe();
        Thread::spawn(move|| {
            for incoming_stream in acceptor.incoming() {
                match incoming_stream {
                    Ok(stream) => server_tx.send(Arc::new(Mutex::new(stream))).unwrap(),
                    Err(ref e) if e.kind() == ErrorKind::NotConnected => break,
                    Err(e) => panic!("Unexpected error: {}", e),
                }
            }
        });
    }

    fn handle_client(&self, stream: Arc<Mutex<TcpStream>>);
    fn make_pipe(&mut self) -> Sender<Arc<Mutex<TcpStream>>>;
    fn run(&self);
}

pub struct InternodeServer {
    hostname: String,
    client_rx: Option<Receiver<Arc<Mutex<TcpStream>>>>,
}

impl Server for InternodeServer {
    fn new(port: u16) -> InternodeServer {
        let hostname = format!("127.0.0.1:{}", port);
        InternodeServer {
            hostname: hostname,
            client_rx: None,
        }
    }

    fn make_pipe(&mut self) -> Sender<Arc<Mutex<TcpStream>>> {
        let (server_tx, client_rx) = channel();
        self.client_rx = Some(client_rx);
        server_tx
    }

    fn hostname(&self) -> &String {
        &self.hostname
    }

    fn handle_client(&self, stream_arc: Arc<Mutex<TcpStream>>) {
        println!("Received connection");
        let mut stream = stream_arc.lock().unwrap();
        let response = b"Hello\r\n";
        let _ = stream.write_all(response);
        let _ = drop(stream);
    }

    fn run(&self) {
        loop {
            match self.client_rx.as_ref().unwrap().recv() {
                Ok(stream) => self.handle_client(stream),
                Err(RecvError) => break,
            }
        }
    }
}

fn main() {
    let mut s = <InternodeServer as Server>::new(10101);
    s.initialize(false);
    s.run();
}

Here's a smaller reproduction of the error: 这是该错误的较小再现:

use std::thread::Thread;

trait Server {
    fn initialize(&self) {
        Thread::spawn(move || self.handle_client());
    }

    fn handle_client(&self);
}

fn main() {}

The problem is that the argument passed to Thread::spawn must be Send . 问题在于传递给Thread::spawn的参数必须是Send You are trying to move self into the closure, but your trait doesn't guarantee Send , so the closure can't be Send . 您正在尝试将self移入闭包,但是您的特征不能保证Send ,因此闭包不能为Send

We can attempt to go down that path with trait Server: Send , but then we get "cannot infer an appropriate lifetime" errors because Send also requires 'static ( for now ). 我们可以尝试使用trait Server: Send来走那条路,但是随后我们会收到“无法推断适当的生存期”错误,因为Send还需要'static目前 )。 Also, it seems very strange to move yourself into a closure. 同样, 将自己置于封闭状态似乎很奇怪。

Really, I think you want to split up your code. 确实,我认为您想拆分代码。 Move handle_client into a separate trait and then ensure that implementations of that trait are Send : handle_client移到单独的特征中,然后确保该特征的实现为Send

use std::thread::Thread;

trait Server {
    fn initialize<D>(&self, driver: D)
        where D: Driver + Send
    {
        Thread::spawn(move || driver.handle_client());
    }
}

trait Driver {
    fn handle_client(&self);
}

fn main() {}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM