Struct pointing on each other


#1

In C, it happens often that 2 structs keep pointer on each other.

How to do something similar in rust ? Here is code example. I would like to keep a pointer on server in my client struct to be able to get information from the server.

struct Server {
    id: u32,
    clients: Vec<Client>,
    next_client_id: u32,
}

impl Server {
    fn new() -> Server {
        Server {
            id: 0,
            clients: Vec::new(),
            next_client_id: 1,
        }
    }

    fn new_client(&mut self) -> Client {
        let client = Client{
            id: self.next_client_id,
        };
        self.next_client_id += 1;
        return client;
    }
}

struct Client {
    id: u32,

    // How to keep pointer on Server ?
}

impl Client {
    fn process_req(&self) {
        //I need the server ID here. How can I obtain it ?
    }
}

fn main() {
    let mut server = Server::new();
    let client = server.new_client();

    client.process_req();
}

Side question : How to paste code and see it as code and not as simple text ? Some part is code, other is not, why ?


#2

Surround code with 3 backticks (``` code here ```) or [code] code here [/code] markup.


#3

It’s easiest if you use Rc/Arc for 2-way relationships. You can’t use references for this unless one strictly and obviously always outlives the other.

Consider also not keeping the other pointer, and e.g. passing it as an argument to functions.

There’s an article that goes way too deep into why you can’t just use C-like pointers and have safety guarantees at the same time: http://cglab.ca/~abeinges/blah/too-many-lists/book/


#4

As @kornel mentioned, you don’t want to do this in Rust - it’s going to give you nothing but grief.

To share state, you externalize the state and share that via Rc/Arc. So for example with your code:

use std::rc::Rc;

struct Server {
    id: u32,
    clients: Vec<Client>,
    next_client_id: u32,
    shared: SharedState,
}

impl Server {
    fn new() -> Server {
        Server {
            id: 0,
            clients: Vec::new(),
            next_client_id: 1,
            shared: Default::default(),
        }
    }

    fn new_client(&mut self) -> Client {
        let client = Client {
            id: self.next_client_id,
            shared: self.shared.clone(),
        };
        self.next_client_id += 1;
        return client;
    }
}

#[derive(Clone, Default)]
struct SharedState {
    inner: Rc<Inner>,
}

#[derive(Default)]
struct Inner {
    // shared stuff here
}

struct Client {
    id: u32,
    shared: SharedState,
}

impl Client {
    fn process_req(&self) {
        // have access to shared state here
    }
}

fn main() {
    let mut server = Server::new();
    let client = server.new_client();
    client.process_req();
}

Server and Clients share ownership of SharedState. If you need to mutate the shared state, then you will need to wrap it in a RefCell (for single threaded) or a Mutex (multithreaded concurrent access). This is a common technique you’ll see often for long-lived objects.