Rust + mio tcp client: explicit lifetime required in the type of connection - rust

I want to implement tcp client using mio. This is my code:
pub struct Client<'a> {
pub connected: bool,
connection: Option<&'a TcpStream>,
auth_handler: auth::AuthHandler<'a>,
}
impl Client<'_> {
pub fn connect(&mut self, host: &str, port: i16) {
let addr_list = format!("{}:{}", host, port)
.to_socket_addrs()
.unwrap();
let addr = addr_list.last().unwrap();
match TcpStream::connect(addr) {
Ok(stream) => {
self.connected = true;
self.connection = Some(&stream);
self.auth_handler.init(self.connection.unwrap());
self.auth_handler.authenticate("login".to_string(), "password".to_string());
println!("Connected to {}:{}", host, port);
}
Err(..) => {
println!("Cannot connect !");
}
}
}
pub fn listen(&mut self) {
let mut connection = self.connection.as_mut().unwrap();
let mut poll = Poll::new().unwrap();
let mut events = Events::with_capacity(256);
poll.registry().register(
connection,
CLIENT,
Interest::READABLE | Interest::WRITABLE
);
loop {
poll.poll(&mut events, None).unwrap();
for event in events.iter() {
match event.token() {
CLIENT => {
if event.is_writable() {
// ...
}
if event.is_readable() {
println!("Data")
}
},
_ => (),
}
}
}
}
pub fn new<'a>() -> Client<'a> {
Client {
connected: false,
connection: None,
auth_handler: auth::AuthHandler {
connection: None::<&'a TcpStream>,
},
}
}
}
and code of my auth handler:
pub struct AuthHandler<'a> {
pub connection: Option<&'a TcpStream>,
}
impl AuthHandler {
pub fn authenticate(&self, login: String, password: String) {
// ...
}
pub fn new<'a>() -> AuthHandler<'a> {
AuthHandler {
connection: None::<&'a TcpStream>,
}
}
pub fn init(&mut self, connection: &TcpStream) {
self.connection = Some(&connection); // error here see ERRORS below
}
}
on compile I got an error "error[E0621]: explicit lifetime required in the type of connection":
self.connection = Some(&connection);
^^^^^^^^^^^^^^^^^ lifetime `'static` required
how to fix it ? From my side I am not sure if static lifetime is OK since I want to destroy connection once authenticated and logged in.

The lifetime of connection is really 'a, but from the code you've posted nobody owns the TcpStream. You could fix the specific error you're having by using the AuthHandlers lifetime:
impl<'a> AuthHandler<'a> {
pub fn authenticate(&self, login: String, password: String) {
// ...
}
pub fn new() -> AuthHandler {
AuthHandler {
connection: None,
}
}
pub fn init(&mut self, connection: &'a TcpStream) {
self.connection = Some(connection);
}
}
But I would expect that you would then get other errors, because in effect Client is a self-referential struct.
So one way to share the connection between these objects would be to put it inside a Arc<Mutex<TcpStream>> and share that between your two objects:
pub struct Client<'a> {
pub connected: bool,
connection: Option<Arc<Mutex<TcpStream>>>,
auth_handler: auth::AuthHandler<'a>,
}
pub struct AuthHandler {
pub connection: Option<Arc<Mutex<TcpStream>>>,
}
then the TcpStream will stay alive as long as you need it. Of course, you would need to .lock() it whenever you use it.
Another option would be to only pass the TcpStream to AuthHandler when it uses it:
pub struct AuthHandler;
impl AuthHandler {
pub fn authenticate(&self, login: String, password: String, stream: &TcpStream) {
// ...
}
}

Related

How to use the typestate pattern in other struct

I want to use the typestate pattern to define several states that allow some exclusive operations on each of them.
I'm using traits instead of an enum to allow further customizations.
So, I'm able to use this pattern until I try to include it inside a struct (the Session part) that is mutated when files are added, changed or removed.
trait IssueState {}
struct Open;
impl IssueState for Open {}
struct WIP {
elapsed_time: u32,
}
impl IssueState for WIP {}
struct Closed {
elapsed_time: u32,
}
impl IssueState for Closed {}
struct Issue<T: IssueState + ?Sized> {
state: Box<T>,
comments: Vec<String>,
}
impl<T: IssueState> Issue<T> {
pub fn comment<S: Into<String>>(&mut self, comment: S) -> &mut Self {
self.comments.push(comment.into());
self
}
}
impl Issue<Open> {
pub fn new() -> Self {
Self {
state: Box::new(Open),
comments: vec![],
}
}
pub fn start(self) -> Issue<WIP> {
Issue {
state: Box::new(WIP { elapsed_time: 0 }),
comments: self.comments,
}
}
}
impl Issue<WIP> {
pub fn work(&mut self, time: u32) -> &mut Self {
self.state.elapsed_time += time;
self
}
pub fn done(self) -> Issue<Closed> {
let elapsed_time = self.state.elapsed_time;
Issue {
state: Box::new(Closed { elapsed_time }),
comments: self.comments,
}
}
}
impl Issue<Closed> {
pub fn elapsed(&self) -> u32 {
self.state.elapsed_time
}
}
struct Session<T: IssueState> {
user: String,
current_issue: Issue<T>,
}
impl<T: IssueState> Session<T> {
pub fn new<S: Into<String>>(user: S, issue: Issue<T>) -> Self {
Self {
user: user.into(),
current_issue: issue,
}
}
pub fn comment<S: Into<String>>(&mut self, comment: S) {
self.current_issue.comment(comment);
}
}
impl Session<WIP> {
pub fn work(&mut self, time: u32) {
self.current_issue.work(time);
}
}
trait Watcher {
fn watch_file_create(&mut self);
fn watch_file_change(&mut self);
fn watch_file_delete(&mut self);
}
impl<T: IssueState> Watcher for Session<T> {
fn watch_file_create(&mut self) {
self.current_issue = Issue::<Open>::new();
}
fn watch_file_change(&mut self) {}
fn watch_file_delete(&mut self) {}
}
fn main() {
let open = Issue::<Open>::new();
let mut wip = open.start();
wip.work(10).work(30).work(60);
let closed = wip.done();
println!("Elapsed {}", closed.elapsed());
let mut session = Session::new("Reviewer", closed);
session.comment("It is OK");
session.watch_file_create();
}
Rust Playground (original)
Rust Playground (edited)
What can I do to fix the problems?
Is the typestate pattern limited to only some situations that do not depend a lot on external events? I mean, I'm trying to use it for processing events, but is it a dead end?, why?
Your Session has a Issue<dyn IssueState> member, but you want to implement its work method by calling Issue<WIP>'s work method. The compiler complains, because an Issue<dyn IssueState> is not (necessarily) a Issue<WIP> and so does not implement that method.

Not clear how to correctly define lifetime for struct

I have TCP Client, which process some handlers. I need to share data between them, so I implemented Session struct for this case:
struct Session<'a> {
pub session_key: Option<&'a Vec<u8>>,
pub username: Option<&'a str>,
pub password: Option<&'a str>,
}
impl Session {
pub fn new() -> Self {
Self {
session_key: None,
username: None,
password: None,
}
}
}
I added access by reference to allow data be borrowed.
But, when I try to use this struct inside my code, I got errors related to lifetime:
implicit elided lifetime not allowed here
or
cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
This is minimal sandbox implementation.
Just in case this is my sample code (mostly it's similar to my real app):
// data which store values that I need to share between handlers
struct Session<'a> {
pub session_key: Option<&'a Vec<u8>>,
pub username: Option<&'a str>,
pub password: Option<&'a str>,
}
// not sure if this correct to set &None below, where to put the lifetime here ?
impl Session {
pub fn new() -> Self {
Self {
session_key: None,
username: None,
password: None,
}
}
}
// what handler return
pub enum HandlerResponse {
Packet(Vec<u8>),
Void,
}
// handler params
pub struct HandlerInput<'a> {
session: &'a mut Session<'a>,
data: Option<Vec<u8>>
}
pub struct Client<'a> {
stream: Option<TcpStream>,
buffer: [u8; 4096],
session: Session<'a>,
}
// public methods
impl<'a> Client<'a> {
pub fn new() -> Self {
Self {
stream: None,
buffer: [0u8; 4096],
session: Session::new(),
}
}
pub fn connect(&mut self, host: &str, port: i16) {
let addr = format!("{}:{}", host, port);
match TcpStream::connect(&addr) {
Ok(stream) => {
self.stream = Some(stream);
println!("Connected to {}", addr);
},
_ => {
println!("Cannot connect");
},
}
}
pub fn handle_connection(
&mut self,
handlers: Vec<fn(HandlerInput
) -> Result<u8, Error>>) {
for handler in handlers {
self.process(handler);
}
}
}
// private methods
impl<'a> Client<'a> {
fn process<F>(&mut self, handler: F)
where
F: Fn(HandlerInput) -> Result<HandlerResponse, Error>
{
let response: Result<HandlerResponse, Error> = match self.handle_read() {
Ok(server_response) => handler(HandlerInput {
session: &mut self.session,
data: Some(server_response),
}),
_ => handler(HandlerInput {
session: &mut self.session,
data: None,
})
};
match response.unwrap() {
HandlerResponse::Packet(data) => {
self.handle_write(data).unwrap();
},
HandlerResponse::Void => {},
}
}
fn handle_write(&mut self, packet: Vec<u8>) -> Result<(), Error> {
let mut stream = self.stream.as_ref().unwrap();
match stream.write(&packet) {
Ok(_) => Ok(()),
_ => Err(Error::new(ErrorKind::Other, "Write error")),
}
}
fn handle_read(&mut self) -> Result<Vec<u8>, Error> {
let mut stream = self.stream.as_ref().unwrap();
let mut buffer = self.buffer.as_mut();
match stream.read(&mut buffer) {
Ok(bytes_count) => {
return Ok(buffer[ .. bytes_count].to_vec());
},
_ => Err(Error::new(ErrorKind::Other, "Read error")),
}
}
}
When I put lifetime on Client struct and then put lifetime on &'a mut self I got an error:
cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
Could you explain how to fix lifetimes here ?
UPDATED:
I need a reference inside Session because I want to use the values of the session inside handlers, if I not use reference, I got the error:
move occurs because input.session.session_key has type Option<Vec<u8>>, which does not implement the Copy trait
The code how I use the Session:
fn handler(input: HandlerInput) {
let hasher = Sha1::new();
let digest = hasher
.chain(input.session.username.unwrap())
.chain(input.session.session_key.unwrap())
.finalize();
}
Also sometimes I need to modify the input inside handler, something like:
let mut session = input.session;
session.session_key = Some(srp_client.session_key());
I can see two problems:
Your short sample code is missing lifetime annotations
Your longer code is an implementation that is almost impossible to achieve with borrows, and therefore I suspect that this question is an XY-problem.
Quick fix of your short code sample
Your impl is missing lifetimes:
pub struct Session<'a> {
pub session_key: Option<&'a Vec<u8>>,
pub username: Option<&'a str>,
pub password: Option<&'a str>,
}
impl<'a> Session<'a> {
pub fn new() -> Self {
Self {
session_key: None,
username: None,
password: None,
}
}
}
What you probably actually want
So my question is, why use borrows in the first place for Session?
You technically only need the reference in HandlerInput. Then, both handlers see the same Session object to modify.
use std::io::{Error, ErrorKind, Read, Write};
use std::net::TcpStream;
// data which store values that I need to share between handlers
struct Session {
pub session_key: Option<Vec<u8>>,
pub username: Option<String>,
pub password: Option<String>,
}
// not sure if this correct to set &None below, where to put the lifetime here ?
impl Session {
pub fn new() -> Self {
Self {
session_key: None,
username: None,
password: None,
}
}
}
// what handler return
pub enum HandlerResponse {
Packet(Vec<u8>),
Void,
}
// handler params
pub struct HandlerInput<'a> {
session: &'a mut Session,
data: Option<Vec<u8>>,
}
pub struct Client {
stream: Option<TcpStream>,
buffer: [u8; 4096],
session: Session,
}
// public methods
impl Client {
pub fn new() -> Self {
Self {
stream: None,
buffer: [0u8; 4096],
session: Session::new(),
}
}
pub fn connect(&mut self, host: &str, port: i16) {
let addr = format!("{}:{}", host, port);
match TcpStream::connect(&addr) {
Ok(stream) => {
self.stream = Some(stream);
println!("Connected to {}", addr);
}
_ => {
println!("Cannot connect");
}
}
}
pub fn handle_connection(
&mut self,
handlers: Vec<fn(HandlerInput) -> Result<HandlerResponse, Error>>,
) {
for handler in handlers {
self.process(handler);
}
}
}
// private methods
impl Client {
fn process<F>(&mut self, handler: F)
where
F: Fn(HandlerInput) -> Result<HandlerResponse, Error>,
{
let response: Result<HandlerResponse, Error> = match self.handle_read() {
Ok(server_response) => handler(HandlerInput {
session: &mut self.session,
data: Some(server_response),
}),
_ => handler(HandlerInput {
session: &mut self.session,
data: None,
}),
};
match response.unwrap() {
HandlerResponse::Packet(data) => {
self.handle_write(data).unwrap();
}
HandlerResponse::Void => {}
}
}
fn handle_write(&mut self, packet: Vec<u8>) -> Result<(), Error> {
let mut stream = self.stream.as_ref().unwrap();
match stream.write(&packet) {
Ok(_) => Ok(()),
_ => Err(Error::new(ErrorKind::Other, "Write error")),
}
}
fn handle_read(&mut self) -> Result<Vec<u8>, Error> {
let mut stream = self.stream.as_ref().unwrap();
let mut buffer = self.buffer.as_mut();
match stream.read(&mut buffer) {
Ok(bytes_count) => {
return Ok(buffer[..bytes_count].to_vec());
}
_ => Err(Error::new(ErrorKind::Other, "Read error")),
}
}
}
fn handler1(input: HandlerInput) -> Result<HandlerResponse, Error> {
Ok(HandlerResponse::Void)
}
fn handler2(input: HandlerInput) -> Result<HandlerResponse, Error> {
Ok(HandlerResponse::Void)
}
fn main() {
let mut client = Client::new();
client.connect("127.0.0.1", 8080);
client.handle_connection(vec![handler1, handler2]);
}
Compiles fine and should work.

Rust + mio: got "move occurs value because" when store TcpStream inside struct field

I want to implement TCP client using struct:
use std::error::Error;
use mio::net::{TcpListener, TcpStream};
use mio::{Events, Interest, Poll, Token};
use std::io::Read;
const CLIENT: Token = Token(0);
pub struct Client {
pub connected: bool,
connection: Option<TcpStream>,
}
impl Client {
pub fn connect(&mut self, host: &str, port: i16) {
let addr = format!("{}:{}", host, port).parse().unwrap();
if let Ok(stream) = TcpStream::connect(addr) {
self.connected = true;
self.connection = Some(stream);
} else {
println!("Cannot connect !");
}
}
pub fn listen(&mut self) {
let mut connection = self.connection.unwrap();
let mut poll = Poll::new().unwrap();
let mut events = Events::with_capacity(256);
poll.registry().register(
&mut connection,
CLIENT,
Interest::READABLE | Interest::WRITABLE
);
loop {
// ...
}
}
pub fn new() -> Client {
Client {
connected: false,
connection: None,
}
}
}
But I got an error:
let mut connection = self.connection.unwrap();
^^^^^^^^^^^^^^^ move occurs because `self.connection` has type `Option<mio::net::TcpStream>`, which does not implement the `Copy` trait
How can I fix this ?

How to set a field in a struct with an empty value?

I am writing a TCP client and have a conn field in my client struct. My client implements two methods new to instantiate the struct and connect to open a connection to the server and set that as the value of the conn field
pub struct FistClient {
addr: String,
conn: TcpStream,
}
impl FistClient {
pub fn new(ip: &str, port: &str) -> Self {
FistClient {
addr: String::from(ip) + ":" + &String::from(port),
// conn: <some-defaullt-value>,
}
}
pub fn connect(&mut self, ip: &str, port: &str) {
let res = TcpStream::connect(&self.addr);
match res {
Ok(c) => self.conn = c,
Err(_) => panic!(),
}
}
}
I want to set the conn field in the new method to some default value. In Go I can do something like conn: nil but it doesn't work here. I tried Default::default() too but that trait isn't implemented for TCPStream how should I set it to a default value?
There's no null in Rust (and no Null Pointer Exception, Rust is designed for safety).
You must either
1) use an option (i.e. a field of type Option<TcpStream>)
2) or better: return a result when building the struct
Here, the best option would probably be to connect from inside a function returning a Result<FistClient>, so that you don't have to check whether your struct has a valid stream.
I would do something like this:
pub struct FistClient {
addr: String,
conn: TcpStream,
}
impl FistClient {
pub fn new(ip: &str, port: &str) -> Result<Self> {
let addr = format!("{}:{}", ip, port);
let conn = TcpStream::connect(&addr)?;
Ok(FistClient { addr, conn })
}
}
Side note: It's really preferable to not build your applications with calls to panic, even when you think you're just building a dirty draft. Handle errors instead.
In Rust, the idea of null is modelled with Option. You give a field the type Option<TcpStream> to indicate that it might not be there (None), or be a valid value (Some(TcpStream)).
pub struct FistClient {
addr: String,
conn: Option<TcpStream>,
}
impl FistClient {
pub fn new(ip: &str, port: &str) -> Self {
FistClient {
addr: String::from(ip) + ":" + &String::from(port),
conn: None,
}
}
pub fn connect(&mut self, ip: &str, port: &str) {
let res = TcpStream::connect(&self.addr);
match res {
Ok(c) => self.conn = Some(c),
Err(_) => panic!(),
}
}
}
You will need to change your type to Option<TCPStream> if you would like to keep this call pattern. an Option expresses the possible lack of a value (I. e. null) with two enum variants: Some(_) and None.
Once you have this in place you can easily retrieve a mutable reference to the inner member by calling as_mut to retrieve an Option<&mut T>.

Rust different return types with same base structure

I want to return a different IMAP connection depending on the secure variable, but imap::client::Client returns a different type if we use SSL or not. All of the functions in Client are implemented by impl<T: Read + Write> Client<T>.
Is there a better and way more efficient solution possible?
use error::*;
use imap::client::Client;
use openssl::ssl::{SslConnectorBuilder, SslMethod, SslStream};
use std::net::TcpStream;
pub enum ConnectionResult {
Normal(Client<TcpStream>),
Secure(Client<SslStream<TcpStream>>),
}
/// Mail account
#[derive(Debug, Deserialize)]
pub struct Account {
pub username: String,
pub password: String,
pub domain: String,
pub port: u16,
pub secure: bool,
}
impl Account {
pub fn connect(&self) -> Result<ConnectionResult> {
if self.secure {
let ssl_connector = SslConnectorBuilder::new(SslMethod::tls())
.chain_err(|| "fail with ssl")?
.build();
let mut imap_socket = Client::secure_connect(
(self.domain.as_str(), self.port),
&self.domain,
ssl_connector,
);
imap_socket
.login(&self.username, &self.password)
.chain_err(|| "fail when login")?;
Ok(ConnectionResult::Secure(imap_socket))
} else {
let mut imap_socket = Client::connect((self.domain.as_str(), self.port))?;
imap_socket
.login(&self.username, &self.password)
.chain_err(|| "fail when login")?;
Ok(ConnectionResult::Normal(imap_socket))
}
}
I just want to return a Client struct, not a enum that holds different Clients:
pub fn connect<T: Read + Write>(&self) -> Result<Client<T>> {
// But this won't work
}
Your approach does not seem bad. My suggest is to use composition: encapsulate your enum in a struct and do the thing you want in each method with a match:
enum ConnectionResult { // not pub because intern implementation
Normal(Client<TcpStream>),
Secure(Client<SslStream<TcpStream>>),
}
pub struct MyClient {
connection_result: ConnectionResult,
// other fields
}
impl MyClient {
pub fn do_something(&self) {
match connection_result {
Normal(client) => // do something with normal client
Secure(client) => // do something with secure client
}
}
}
From the user point of view, there is no difference between the two clients.
If you do not want this solution, you can use the nightly -> impl feature:
#![feature(conservative_impl_trait)]
trait MyClient {
// the methods you need
}
struct NormalClient {
client: Client<TcpStream>,
/*etc.*/
}
struct SecureClient {
client: Client<SslStream<TcpStream>>,
/*etc.*/
}
impl MyClient for NormalClient { /*etc.*/ }
impl MyClient for SecureClient { /*etc.*/ }
fn get_client() -> impl MyClient { /*etc.*/ }

Resources