Hello everyone,
whenever i think i finally got how lifetimes work, i find myself in a situtation fighting with the compiler with lifetimes
#[tokio::main]
async fn main() -> io::Result<()> {
let mut listener = TcpListener::bind("127.0.0.1:2030").await?;
let users: Vec<User> = Vec::new();
loop {
let (mut stream, addr) = listener.accept().await?;
tokio::spawn(async move {
let (reader, writer) = stream.split();
let user = User::new(reader, writer, addr);
user.ask("What's your name?").await;
});
}
}
#[derive(Debug)]
pub struct User<T> {
pub reader: ReadHalf<T>,
pub writer: WriteHalf<T>,
pub addr: SocketAddr,
pub username: String,
}
impl<T> User<T>
where T: AsyncRead + AsyncWrite {
pub async fn new(reader: ReadHalf<'_>, writer: WriteHalf<'_>, addr: SocketAddr) -> Self {
Self {
reader,
writer,
addr,
username: String::new(),
}
}
pub async fn ask(&mut self, quest: &str) -> io::Result<()> {
let quest = format!(" >>> {} ", quest);
self.writer.write_all(quest.as_str().as_bytes()).await?;
Ok(())
}
}
anyone can help me please (with some explanation) on how can i send reader and writer to User::new , and store them as fields in struct for later use?
alice
March 25, 2020, 10:02pm
2
Are you using TcpStream::split
or tokio::io::split
? And what is the error?
yes i am ysing tcpstream::split
the error
warning: unused import: `tokio::io::AsyncBufReadExt` [21/21]
--> src/user.rs:7:5
|
7 | use tokio::io::AsyncBufReadExt;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
warning: unused import: `tokio::net::TcpStream`
--> src/user.rs:8:5
|
8 | use tokio::net::TcpStream;
| ^^^^^^^^^^^^^^^^^^^^^
warning: unused import: `tokio::io::BufReader`
--> src/user.rs:11:5
|
11 | use tokio::io::BufReader;
| ^^^^^^^^^^^^^^^^^^^^
error[E0106]: missing lifetime specifier
--> src/user.rs:15:17
|
15 | pub reader: ReadHalf<T>,
| ^^^^^^^^^^^ expected lifetime parameter
error[E0106]: missing lifetime specifier
--> src/user.rs:16:17
|
16 | pub writer: WriteHalf<T>,
| ^^^^^^^^^^^^ expected lifetime parameter
error[E0107]: wrong number of type arguments: expected 0, found 1
--> src/user.rs:15:26
|
15 | pub reader: ReadHalf<T>,
| ^ unexpected type argument
error[E0107]: wrong number of type arguments: expected 0, found 1
--> src/user.rs:16:27
|
16 | pub writer: WriteHalf<T>,
| ^ unexpected type argument
error: cannot infer an appropriate lifetime
--> src/user.rs:22:22
|
22 | pub async fn new(reader: ReadHalf<'_>, writer: WriteHalf<'_>, addr: SocketAddr) -> Self {
| ^^^^^^ ...but this borrow...
23 | Self {
24 | reader,
| ------ this return type evaluates to the `'static` lifetime...
|
note: ...can't outlive the lifetime `'_` as defined on the method body at 22:39
--> src/user.rs:22:39
|
22 | pub async fn new(reader: ReadHalf<'_>, writer: WriteHalf<'_>, addr: SocketAddr) -> Self {
| ^^
error: cannot infer an appropriate lifetime
--> src/user.rs:22:44
|
22 | pub async fn new(reader: ReadHalf<'_>, writer: WriteHalf<'_>, addr: SocketAddr) -> Self {
| ^^^^^^ ...but this borrow...
...
25 | writer,
| ------ this return type evaluates to the `'static` lifetime...
|
note: ...can't outlive the lifetime `'_` as defined on the method body at 22:39
--> src/user.rs:22:39
|
22 | pub async fn new(reader: ReadHalf<'_>, writer: WriteHalf<'_>, addr: SocketAddr) -> Self {
| ^^
error: aborting due to 6 previous errors
Some errors have detailed explanations: E0106, E0107.
For more information about an error, try `rustc --explain E0106`.
error: could not compile `mixed`.
To learn more, run the command again with --verbose.
alice
March 25, 2020, 10:08pm
4
That halves returned by that split function are more or less just references into the corresponding stream, which means that if you put them in a struct, that struct now contains a reference to the tcp stream, and needs lifetimes for that reason. It looks like this:
#[derive(Debug)]
pub struct User<'a> {
pub reader: ReadHalf<'a>,
pub writer: WriteHalf<'a>,
pub addr: SocketAddr,
pub username: String,
}
impl<'a> User<'a> {
pub fn new(reader: ReadHalf<'a>, writer: WriteHalf<'a>, addr: SocketAddr) -> Self {
...
}
}
Note that the halves are specific to TcpStream
, so they are not generic over the type of stream. Additionally you should be aware that the halves being references all of the consequences of references, e.g. you can't send them to separate tasks, and the tcp stream must be in scope for the full duration of your User
struct (e.g. you can't put it in a vector where the tcp stream is not in scope).
Unless the above works in your situation, you should probably consider not splitting the stream and just keeping the TcpStream
object around.
2 Likes
i tried to use 'a
as suggested above but received another error
error[E0308]: mismatched types
--> src/user.rs:23:9
|
23 | / Self {
24 | | reader,
25 | | writer,
26 | | addr,
27 | | username: String::new(),
28 | | }
| |_________^ lifetime mismatch
|
= note: expected struct `user::User<'static>`
found struct `user::User<'a>`
note: the lifetime `'a` as defined on the impl at 21:6...
--> src/user.rs:21:6
|
21 | impl<'a> User<'a> {
| ^^
= note: ...does not necessarily outlive the static lifetime
inejge
March 26, 2020, 11:43am
6
User::new()
doesn't need to be async
, since it's not doing any I/O. With that taken care of, the rest isn't too difficult. Here's a playground which reads the name and prints it out.
1 Like
@inejge assume i am calling .await inside fn new
which will require new to be async
in this case what is the rule of the lifetime?
inejge
March 26, 2020, 8:32pm
8
In this case, you could return User<'a>
instead of Self
in an async fn new
. (Then, you'd additionally have to call .await
after new()
.) The problem with Self
is that you can't parametrize it either with a type or a lifetime, and the return type of an async fn
needs to capture all input lifetimes. Generally, this part of the language is still rather brittle and unergonomic.
system
Closed
June 24, 2020, 8:32pm
9
This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.