Hello everyone.
I am in need of help regarding lifetimes (or software design?) in structs. I am trying to code a "simple" chat server in order to learn rust. I already understood so much! The design is a classic un-optimized 1 client = 1 server thread, with 2 other server threads: the client acceptance loop, and the Message dispatching loop. I will investigate the more standard select(2)
event loop later on. Baby steps.
// Message flow
[client_app] -> [client server thread] -> (via shared sync::mpsc::Sender<Message>) -> [dispatch server loop] -> (via User's sync::mpsc::Sender<Message>) -> [client server thread] -> [client_app]
However, i have hit a stop as i am faced with the following situation:
Considering the following structs:
use std::sync::Mutex;
use std::sync::mpsc::Sender;
/// Represent a user, server side
struct User {
name: [u8; 32],
client_tx: Mutex<Sender<Message>>, // a Message is simply 3 [u8] arrays: source, target, content
}
/// Represents a channel, with its members.
/// I had hoped to just store references to the actual User(s), which are stored in the Server struct
struct Channel<'a> {
name: [u8; 32],
members: Vec<&'a User>,
}
struct Server<'a> {
channels: Vec<Channel<'a>>,
users: Vec<User>,
}
NOTE: I first thought of the actual User
storage in the server in order to enable User
s to join multiple channels. I realize that there's no way for rust to ensure the proper removal of all the Channel
's references on client deletion, hence my doubts regarding my approach.
however, when trying to write the code enabling a User
to join a Channel
, i am getting lifetime errors:
impl Server<'_> {
fn add_user_to_channel(&mut self, user_name: &str, channel_name: &str) {
let user = self
.users
.iter()
.find(|u| u.name == user_name.as_bytes())
.expect("Received message from user not present in server's user table...");
let channel = self
.channels
.iter_mut()
.find(|&&mut c| c.name == channel_name.as_bytes());
if channel.is_some() {
channel.unwrap().members.push(user);
}
}
}
I am getting the following errors (line numbers not accurate, the code has been purged of non-essential info):
--> src/server.rs:48:14
|
48 | .iter()
| ^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 45:5...
--> src/server.rs:45:5
|
45 | / fn add_user_to_channel(&mut self, user_name: &str, channel_name: &str) {
46 | | let user = self
47 | | .users
48 | | .iter()
... |
57 | | }
58 | | }
| |_____^
note: ...so that reference does not outlive borrowed content
--> src/server.rs:46:20
|
46 | let user = self
| ____________________^
47 | | .users
| |__________________^
note: but, the lifetime must be valid for the lifetime `'_` as defined on the impl at 44:13...
--> src/server.rs:44:13
|
44 | impl Server<'_> {
| ^^
note: ...so that reference does not outlive borrowed content
--> src/server.rs:56:43
|
56 | channel.unwrap().members.push(user);
| ^^^^
So i was wondering: is there a way to tell rustc
that the channel.members
references will be deleted before the real server.user
? Or am i going entirely in the wrong direction?
I thank you for reading this far, and apologize if things aren't clear enough. Please tell me if I can provide any more information or if sections need cleaning.
paul