Accessing variable in other function

I’ve the main.rs as below, where I defined a vec clients to hold all the connected sockets, but I failed to add the sockets to it upon getting them opened in the onopen function, that is defined in another file.

main.rs

#[derive(Debug)]
pub struct Truck {
    socket: Sender,
    unique_id: String
}

#[derive(Debug)]
pub struct Clients {
    pub client: Vec<Truck>
}

impl Clients {
    pub fn new() -> Self { Clients { client: Vec::new() } }

    pub fn insert(&mut self, t: Truck) {
        self.client.push(t)
    }
}

fn main() {
    let mut clients:Clients = Clients::new();  // to hold vec of all connected sockets
    let mut ws = ws::WebSocket::new(
        |out| Client { out }).unwrap();

    thread::spawn(|| { ws.listen("127.0.0.1:8080").unwrap(); });
}

And websocket handler in another file as below:

pub struct Client {
    pub out: Sender,
}

impl Handler for Client {

    fn on_open(&mut self, _: Handshake) -> Result<()> {
        let truck = Truck { socket: self.out.clone(), unique_id: "".to_string() };     // this is ok
        clients.insert(truck);    //  this is not ok, `clients` is not acccessable

        Ok(())
    }

fn on_message(&mut self, msg: Message) -> Result<()> { /*  */ }

fn on_close(&mut self, code: CloseCode, reason: &str) { /*  */ }

Once I tried to define clients as global variable like:

static mut clients:Clients = Clients::new();

I go the below error:

error[E0015]: calls in statics are limited to constant functions, tuple structs and tuple variants
  --> src/main.rs:57:30
   |
57 | static mut clients:Clients = Clients::new();

If you want a global variable, you’ll have to stick it in a Mutex. But I’d strongly recommend against using a global variable. Why don’t you just make the clients be a field in your Client struct? Then it’ll be right there when you want it.

I need to have list of the clients, this is why I created the global one, can I add to a struct without having an instant if it?

You have an instance of Client right here, which is why I suggested putting your list in Client.

Not getting your point, can you help with code.

#[derive(Debug)]
pub struct Truck {
    socket: Sender,
    unique_id: String
}

#[derive(Debug)]
pub struct Clients {
    pub client: Vec<Truck>
}

impl Clients {
    pub fn new() -> Self { Clients { client: Vec::new() } }

    pub fn insert(&mut self, t: Truck) {
        self.client.push(t)
    }
}

fn main() {
    let mut ws = ws::WebSocket::new(|out| Client {
             out,
             clients: Clients::new(),  // <<<<<< created the clients here instead of as a local variable
       }).unwrap();

    thread::spawn(|| { ws.listen("127.0.0.1:8080").unwrap(); });
}

pub struct Client {
    pub out: Sender,
    pub clients: Clients, // <<<<<< inserted this
}

impl Handler for Client {

    fn on_open(&mut self, _: Handshake) -> Result<()> {
        let truck = Truck { socket: self.out.clone(), unique_id: "".to_string() };     // this is ok
        self.clients.insert(truck);    // <<<<< this is now ok

        Ok(())
    }

   fn on_message(&mut self, msg: Message) -> Result<()> { /*  */ }

   fn on_close(&mut self, code: CloseCode, reason: &str) { /*  */ }
}

I’ve just changed four lines of code, marked with // <<<<

Looking at this further, I’m guessing my suggestion may not help you. The problem I’m running into is that you provided a large fragment of incomplete code, without explanation of what your goal is, or what crates (if any) you are trying to interface with. My suggestion will make your code compile, but maybe won’t do what you want. Since I don’t know what you want, I can’t fix that.

I’m trying to have websocket server that is working along with webserver, and I want to have list vec of the connected sockets every tie new socket is opened, then remove it from this vec every time it is closed.
This is my full code:

main.rs

#![feature(proc_macro_hygiene, decl_macro)]
#![feature(const_vec_new)]

#[macro_use] extern crate rocket;
use rocket::config::{Config, Environment};

use std::thread;

use crate::socket::{Client, Clients};
use rocket_contrib::templates::Template;
use std::collections::HashMap;
use rocket_contrib::serve::StaticFiles;
use std::rc::Rc;
use std::cell::Cell;

#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;

#[get("/")]
fn index() -> Template {
    let context = HashMap::<String, String>::new();
    Template::render("index", context)
}

use rocket::request::Form;

#[derive(FromForm)]
struct Truck {
    truck_id: String,
    message: String,
}


#[get("/truck?<truck_id>&<message>")]
fn msg(truck_id: String, message: String) {
    println!("msg recieved");
    println!("truck {} sent {}", truck_id, message);

}

fn main() {
    println!("Hello, world!");

    let mut ws = ws::WebSocket::new(
        |out| Client { out }).unwrap();

    thread::spawn(|| { ws.listen("127.0.0.1:8080").unwrap(); });

    let config = Config::build(Environment::Production)
        .address("127.0.0.1")
        .port(8081)
        .finalize().unwrap();

    rocket::custom(config)     // replaces calls to rocket::ignite() & Rocket.toml
        .mount("/static", StaticFiles::from(concat!(env!("CARGO_MANIFEST_DIR"), "/static")))
        .mount("/", routes![index, msg])
        .attach(Template::fairing())
        .launch();

}

socket.rs

use ws::{Handler, Message, Request, Response, Result, Sender, CloseCode, WebSocket, Handshake};
use std::fs;
use ws::util::Token;
use std::mem::replace;

pub struct Client {
    pub out: Sender,
}

#[derive(Serialize, Deserialize, Debug)]
struct Location {
    unique_id: String,
    lat: f64,
    lon: f64,
    speed: f64
}

#[derive(Debug)]
pub struct Truck {
    socket: Sender,
    unique_id: String
}


use std::default::Default;
use crate::clients;
use core::borrow::Borrow;

#[derive(Debug)]
pub struct Clients {
    pub client: Vec<Truck>
}

impl Clients {
    pub fn new() -> Self { Clients { client: Vec::new() } }

    pub fn insert(&mut self, t: Truck) {
        self.client.push(t)
    }
}



impl Handler for Client {

    fn on_open(&mut self, _: Handshake) -> Result<()> {
        let truck = Truck { socket: self.out.clone(), unique_id: "" };
        unsafe { clients }.insert(truck);
        println!("sockect: {:#?}", unsafe { clients });
        
        Ok(())
    }

    fn on_message(&mut self, msg: Message) -> Result<()> {
        println!("Server got message '{}'. ", msg);

        match msg {
            Message::Text(msg) => {
                let deserialize = if let Ok(deserialize) = serde_json::from_str::<Location>(&msg) {
                    //  println!("socket:{}, id = {:?}", self.out.connection_id(), &deserialize.unique_id);
                    self.out.broadcast(msg);
                };
                Ok(())
            },
            Message::Binary(bin) => {
                Ok(())
            }
        }
    }

    fn on_close(&mut self, code: CloseCode, reason: &str) {
        match code {
            CloseCode::Normal => println!("The client is done with the connection."),
            CloseCode::Away   => println!("The client is leaving the site."),
            _ => println!("The client encountered an error: {}", reason),
        }
    }

    fn on_request(&mut self, req: &Request) -> Result<(Response)> {
        match req.resource() {
            "/ws" => Response::from_request(req),
            "/" => {
                let filename = "index.html";
                let contents = fs::read_to_string(filename).expect("Something went wrong reading the file");
                Ok(Response::new(200, "OK", contents.as_bytes().to_vec()))
            }
            _ => Ok(Response::new(404, "Not Found", b"404 - Not Found".to_vec())),
        }
    }
}
1 Like

Sounds like you probably want a Mutex, since it looks like probably you’ll be reading and writing three Vec from different threads. You could either use lazy_static, it just create the Mutex in main.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.