Mutable borrow in loop over mutable vector issue [design help]

EDIT: i have posted a more clear example without pseudo code in the replies.

I am making a chat bot to learn rust, and I have an issue with the borrow checker, that no matter how I restructure my code, i cannot seem to get past the borrow checker. I am looping over a vector of "Chat" structs that contain a socket in the main event loop, and everything works fine, but if i wanted to access the vector of Chat structs that is being looped over, in some other function, for example the events function in the impl Chat, i am completely unable to pass it anywhere else.

I have tried passing the connections as a variable to the

con.events(collection);

like so,

con.events(collection, self.connections ); // I tried every combination of &/&mut

But the borrow checker always stops me in my tracks. In the code i have included an example of what i can not get to work:

 /*
     * 
    fn send_to_chat(chatname, message){
    showing you what i want to do:
    for i in the connections vector located in Bakery, 
        if i.name == chatname
            i.chat_post(message)
    */

Here is the code, search for the above section in the code to get a clear understanding.

/*
 * TODO: CLEAN UP CODE AND ADD EVENTS.
 */


use std::net::TcpStream;
use std::io::prelude::*;
use rand::Rng;
use std::thread;
use std::time::Duration;
use regex::Regex;
use std::collections::HashMap;
use reqwest::header::USER_AGENT;
use reqwest::header::HeaderValue;
use html_escape::encode_text;
mod rainbow;
use rainbow::Rainbow; //found in extra-stuff repository. i do not own this code.



fn g_server(mut group: String) -> String{

    let weights = [[5, 75],[6, 75],[7, 75],[8, 75],[16, 75],[17, 75],[18, 75],[9, 95],[11, 95],[12, 95],[13, 95],[14, 95],[15, 95],[19, 110],[23, 110],[24, 110],[25, 110],[26, 110],[28, 104],[29, 104],[30, 104],[31, 104],[32, 104],[33, 104],[35, 101],[36, 101],[37, 101],[38, 101],[39, 101],[40, 101],[41, 101],[42, 101],[43, 101],[44, 101],[45, 101],[46, 101],[47, 101],[48, 101],[49, 101],[50, 101],[52, 110],[53, 110],[55, 110],[57, 110],[58, 110],[59, 110],[60, 110],[61, 110],[62, 110],[63, 110],[64, 110],[65, 110],[66, 110],[68, 95],[71, 116],[72, 116],[73, 116],[74, 116],[75, 116],[76, 116],[77, 116],[78, 116],[79, 116],[80, 116],[81, 116],[82, 116],[83, 116],[84, 116]];

    group = group.replace("-","q").replace("_","q");
    let a = if group.len() > 6 {
        let a = if group.len() >= 9 {
            &group[6..9]
        } else {
            &group[6..]
        };
        let a = i64::from_str_radix(a, 36).unwrap() as f64;
        let a = f64::max(1000.0, a);
        a
    }
    else{
        1000.0
    };
    let b = std::cmp::min(5, group.len());
    let b = &group[..b];
    let b = i64::from_str_radix(b, 36).unwrap() as f64;
    let num = (b / a) % 1.0;
    let mut anpan = 0.0;
    let mut s_number = 0;
    let total_weight: f64 = weights.iter().map(|a| a[1] as f64).sum();
    for x in weights {
        anpan += x[1] as f64 / total_weight;
        if num <= anpan {
            s_number += x[0];
            break;
        }
    }

    format!("s{}.chatango.com:443", s_number)
}

fn auth(user: &str, pass: &str) -> String {
    let mut form = HashMap::new();
    form.insert("user_id", user);
    form.insert("password", pass);
    form.insert("storecookie", "on");
    form.insert("checkerrors", "yes");
    let client = reqwest::blocking::Client::new();
    let res = client.post("http://chatango.com/login")
    .form(&form)
    .header(USER_AGENT, HeaderValue::from_static(
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 \
(KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
    ))
    .send();

    let res = res.unwrap();
    let res = res.headers();
    let cookie: Vec<_> = res.get_all("set-cookie").iter().filter_map(|val| val.to_str().ok()).map(|s| s.to_string()).collect();
    let cookie = &cookie[2];
    let re = Regex::new(r"auth.chatango.com=(.*?);").unwrap();
    let extract = re.captures(cookie).unwrap().get(1);
    let extract = extract.unwrap().as_str().to_string();
    extract
}


struct Message{
    user: String,
    cid: String,
    uid: String,
    time: String,
    sid: String,
    ip: String,
    content: String,
    chat: String,
}

struct Chat{
    name: String,
    cumsock: TcpStream,
    wbyte: String,
    byteready: bool
}

impl Chat{
    fn new(name: String, username: String, password: String, ctype: &str) -> Self {
        let server = if ctype == "chat" {
            g_server(name.clone())
        } else {
            let server = "c1.chatango.com:5222".to_string();
            server
        };
        let mut chat = Chat{
            name: name,
            cumsock: TcpStream::connect(server).unwrap(),
            wbyte: "".to_string(),
            byteready: false,
        };
        chat.cumsock.set_nonblocking(true).expect("set_nonblocking call failed");
        if ctype == "chat" {
            chat.chat_login(username, password);
        } else {
            chat.pm_login(username, password);
        };
        chat
    }

    fn chat_login(&mut self, username: String, password: String){

        let chat_id = rand::thread_rng().gen_range(10_u128.pow(15)..10_u128.pow(16)).to_string();
        self.chat_send(vec!["bauth", &self.name.clone(), &chat_id, &username, &password]);
        self.byteready = true;
        let mut socket_clone = self.cumsock.try_clone().expect("Failed to clone socket");
        thread::spawn(move || {
            loop {
                let data = b"\r\n\x00";
                socket_clone.write(data);
                thread::sleep(Duration::from_secs(20));

            }
        });

    }

    fn pm_login(&mut self, username: String, password: String){

        let auth = auth(&username, &password);
        let to_send = format!("tlogin:{}:2\x00", auth);
        let _ = self.cumsock.write(to_send.as_bytes()).unwrap();
        let mut socket_clone = self.cumsock.try_clone().expect("Failed to clone socket");
        thread::spawn(move || {
            loop {
                thread::sleep(Duration::from_secs(20));
                let data = b"\r\n\x00";
                socket_clone.write(data);
            }
        });

    }

    fn chat_send(&mut self, data: Vec<&str>){
        let ending = if self.byteready {
            "\r\n\x00"
        } else{
            "\x00"
        };
        let data = data.join(":");
        let data = format!("{}{}", data, ending);
        self.cumsock.write(data.as_bytes());

    }

    fn chat_post(&mut self, args: &str){
        let message  = format!("<n000000/><f x12000000=\"0\">{}</f>\r\n\x00", args);
        self.chat_send(vec!["bm","derp", "2048", &message]);

    }

    fn events(&mut self, collection: Vec<&str>){
        let event = &collection[0];
        let data = &collection[1..];
        //println!("event: {:?} data: {:?}", event, data);
        if *event == "b"{
            self.event_b(data);

        }
    }

    fn event_b(&mut self, data: &[&str]){
        let user = data[1];
        let alias = data[2];
        let user = if user == "" {
            "None"
        } else{
            user
        };
        let user = if user == "None"{
            if alias == ""{
                "None"
            } else if alias == "None"{
                "None"
            } else{
                alias
            }
        }else
        { user};
        let re = Regex::new(r"<.*?>").unwrap();
        let content = data[9..].join("");
        let content = re.replace_all(&content, "");
        let content = html_escape::decode_html_entities(&content);
        let mut message = Message{
            user: user.to_string(),
            cid: data[4].to_string(),
            uid: data[3].to_string(),
            time: data[0].to_string(),
            sid: data[5].to_string(),
            ip: data[6].to_string(),
            content: content.to_string(),
            chat: self.name.clone(),
        };

        self.on_post(message);


    }
    /*
     * 
    fn send_to_chat(chatname, message){
    showing you what i want to do:
    for i in the connections vector located in Bakery, 
        if i.name == chatname
            i.chat_post(message)
    */
}

    fn on_post(&mut self, message: Message){
        //println!("{}: {}", message.user, message.content);
        if message.content.to_lowercase().contains(""){
            println!("{}: {}: {}", message.user, message.chat, message.content)
        }
        if message.chat != "".to_string(){
            if message.content.starts_with("$") {
                let args = message.content.split(" ");
                let args: Vec<&str> = args.collect();
                let command = args[0];
                let args = if args.len() > 1 {
                    args[1..].join(" ")
                } else {
                    "".to_string()
                };
                let command = command.replace("$", "");
                let command = command.to_lowercase();
                match command.as_str() {
                    "say" => {
                        self.chat_post(&args);
                    }
                    "rainbow" => {
                        let size = "12";
                        let rainbowed = Rainbow::rainbow_text(&args, size);
                        self.chat_post(&rainbowed);
                    }
                    _ => {

                        self.chat_post("Unknown command");
                    }

                }



            }
        }
    }


}


struct Bakery{
    connections: Vec<Chat>,

}

impl Bakery{

    fn oven(username: &str, password: &str, room_list: Vec<&str>) -> Self {
        let mut bakery = Bakery {
            connections: vec![],
        };
        for i in room_list{
            let chat = Chat::new(i.to_string(), username.to_string(), password.to_string(), "chat");

            bakery.connections.insert(bakery.connections.len(), chat);
        };
        let chat = Chat::new("_pm".to_string(), username.to_string(), password.to_string(), "pm");
        bakery.connections.insert(bakery.connections.len(), chat);
        bakery.breadbun();
        bakery

    }

    fn breadbun(&mut self){
        let anpan_is_tasty = true;
        while anpan_is_tasty {
            for con in &mut self.connections{
                let mut buf = [0; 1024];
                if let Ok(len) = con.cumsock.read(&mut buf) {
                    if len > 0 {
                        let data = &buf[..len];
                        for x in data.split(|b| b == &0x00) {
                            let s = String::from_utf8_lossy(x);
                            let s = s.trim();
                            let s = s.split(":");
                            let collection = s.collect::<Vec<&str>>();
                            con.events(collection);
                        }
                    }
                }

            }
        }
    }
}

fn main() {
    Bakery::oven("anpanbot", "", vec![""]);


}

This is pseudo code so I can't see what is causing the borrowing error. Could you please post the actual code you tried, along with the full error from running cargo check in the terminal? If you tried two approaches, post the code and error for each.

where is this Bakery? is it passed in as argument? or is it a field of self? your code is incomplete.

please show the actual code that gives you compile error, and what the error message is, so people can help you fix the error.

don't just show the pseudo code or just a description: how can people fix the problem if they don't see the problem?

here is a more clear example:

/*
 * TODO: CLEAN UP CODE AND ADD EVENTS.
 */


use std::net::TcpStream;
use std::io::prelude::*;
use rand::Rng;
use std::thread;
use std::time::Duration;
use regex::Regex;
use std::collections::HashMap;
use reqwest::header::USER_AGENT;
use reqwest::header::HeaderValue;
use html_escape::encode_text;
mod rainbow;
use rainbow::Rainbow; //found in extra-stuff repository. i do not own this code.



fn g_server(mut group: String) -> String{

    let weights = [[5, 75],[6, 75],[7, 75],[8, 75],[16, 75],[17, 75],[18, 75],[9, 95],[11, 95],[12, 95],[13, 95],[14, 95],[15, 95],[19, 110],[23, 110],[24, 110],[25, 110],[26, 110],[28, 104],[29, 104],[30, 104],[31, 104],[32, 104],[33, 104],[35, 101],[36, 101],[37, 101],[38, 101],[39, 101],[40, 101],[41, 101],[42, 101],[43, 101],[44, 101],[45, 101],[46, 101],[47, 101],[48, 101],[49, 101],[50, 101],[52, 110],[53, 110],[55, 110],[57, 110],[58, 110],[59, 110],[60, 110],[61, 110],[62, 110],[63, 110],[64, 110],[65, 110],[66, 110],[68, 95],[71, 116],[72, 116],[73, 116],[74, 116],[75, 116],[76, 116],[77, 116],[78, 116],[79, 116],[80, 116],[81, 116],[82, 116],[83, 116],[84, 116]];

    group = group.replace("-","q").replace("_","q");
    let a = if group.len() > 6 {
        let a = if group.len() >= 9 {
            &group[6..9]
        } else {
            &group[6..]
        };
        let a = i64::from_str_radix(a, 36).unwrap() as f64;
        let a = f64::max(1000.0, a);
        a
    }
    else{
        1000.0
    };
    let b = std::cmp::min(5, group.len());
    let b = &group[..b];
    let b = i64::from_str_radix(b, 36).unwrap() as f64;
    let num = (b / a) % 1.0;
    let mut anpan = 0.0;
    let mut s_number = 0;
    let total_weight: f64 = weights.iter().map(|a| a[1] as f64).sum();
    for x in weights {
        anpan += x[1] as f64 / total_weight;
        if num <= anpan {
            s_number += x[0];
            break;
        }
    }

    format!("s{}.chatango.com:443", s_number)
}

fn auth(user: &str, pass: &str) -> String {
    let mut form = HashMap::new();
    form.insert("user_id", user);
    form.insert("password", pass);
    form.insert("storecookie", "on");
    form.insert("checkerrors", "yes");
    let client = reqwest::blocking::Client::new();
    let res = client.post("http://chatango.com/login")
    .form(&form)
    .header(USER_AGENT, HeaderValue::from_static(
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 \
(KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
    ))
    .send();

    let res = res.unwrap();
    let res = res.headers();
    let cookie: Vec<_> = res.get_all("set-cookie").iter().filter_map(|val| val.to_str().ok()).map(|s| s.to_string()).collect();
    let cookie = &cookie[2];
    let re = Regex::new(r"auth.chatango.com=(.*?);").unwrap();
    let extract = re.captures(cookie).unwrap().get(1);
    let extract = extract.unwrap().as_str().to_string();
    extract
}


struct Message{
    user: String,
    cid: String,
    uid: String,
    time: String,
    sid: String,
    ip: String,
    content: String,
    chat: String,
}

struct Chat{
    name: String,
    cumsock: TcpStream,
    wbyte: String,
    byteready: bool
}

impl Chat{
    fn new(name: String, username: String, password: String, ctype: &str) -> Self {
        let server = if ctype == "chat" {
            g_server(name.clone())
        } else {
            let server = "c1.chatango.com:5222".to_string();
            server
        };
        let mut chat = Chat{
            name: name,
            cumsock: TcpStream::connect(server).unwrap(),
            wbyte: "".to_string(),
            byteready: false,
        };
        chat.cumsock.set_nonblocking(true).expect("set_nonblocking call failed");
        if ctype == "chat" {
            chat.chat_login(username, password);
        } else {
            chat.pm_login(username, password);
        };
        chat
    }

    fn chat_login(&mut self, username: String, password: String){

        let chat_id = rand::thread_rng().gen_range(10_u128.pow(15)..10_u128.pow(16)).to_string();
        self.chat_send(vec!["bauth", &self.name.clone(), &chat_id, &username, &password]);
        self.byteready = true;
        let mut socket_clone = self.cumsock.try_clone().expect("Failed to clone socket");
        thread::spawn(move || {
            loop {
                let data = b"\r\n\x00";
                socket_clone.write(data);
                thread::sleep(Duration::from_secs(20));

            }
        });

    }

    fn pm_login(&mut self, username: String, password: String){

        let auth = auth(&username, &password);
        let to_send = format!("tlogin:{}:2\x00", auth);
        let _ = self.cumsock.write(to_send.as_bytes()).unwrap();
        let mut socket_clone = self.cumsock.try_clone().expect("Failed to clone socket");
        thread::spawn(move || {
            loop {
                thread::sleep(Duration::from_secs(20));
                let data = b"\r\n\x00";
                socket_clone.write(data);
            }
        });

    }

    fn chat_send(&mut self, data: Vec<&str>){
        let ending = if self.byteready {
            "\r\n\x00"
        } else{
            "\x00"
        };
        let data = data.join(":");
        let data = format!("{}{}", data, ending);
        self.cumsock.write(data.as_bytes());

    }

    fn chat_post(&mut self, args: &str){
        let message  = format!("<n000000/><f x12000000=\"0\">{}</f>\r\n\x00", args);
        self.chat_send(vec!["bm","derp", "2048", &message]);

    }

    fn send_to_chat(&mut self, args: &str, connections: Vec<Chat>){
       let args = args.split(" ");
       let args = args.collect::<Vec<&str>>();
       let room = args[0];
       let message = args[1..].join(" ");
       for i in connections{
           if room.to_string() == i.name{
               i.chat_post(&message);
        }
    }
    }

    fn events(&mut self, collection: Vec<&str>, connections: Vec<Chat>){
        let event = &collection[0];
        let data = &collection[1..];
        //println!("event: {:?} data: {:?}", event, data);
        if *event == "b"{
            self.event_b(data, connections);

        }
    }

    fn event_b(&mut self, data: &[&str],  connections: Vec<Chat>){
        let user = data[1];
        let alias = data[2];
        let user = if user == "" {
            "None"
        } else{
            user
        };
        let user = if user == "None"{
            if alias == ""{
                "None"
            } else if alias == "None"{
                "None"
            } else{
                alias
            }
        }else
        { user};
        let re = Regex::new(r"<.*?>").unwrap();
        let content = data[9..].join("");
        let content = re.replace_all(&content, "");
        let content = html_escape::decode_html_entities(&content);
        let mut message = Message{
            user: user.to_string(),
            cid: data[4].to_string(),
            uid: data[3].to_string(),
            time: data[0].to_string(),
            sid: data[5].to_string(),
            ip: data[6].to_string(),
            content: content.to_string(),
            chat: self.name.clone(),
        };

        self.on_post(message, connections);


    }

    fn on_post(&mut self, message: Message, connections: Vec<Chat>){
        //println!("{}: {}", message.user, message.content);
        if message.content.to_lowercase().contains("herenti"){
            println!("{}: {}: {}", message.user, message.chat, message.content)
        }
        if message.chat != "jewelisland".to_string(){
            if message.content.starts_with("$") {
                let args = message.content.split(" ");
                let args: Vec<&str> = args.collect();
                let command = args[0];
                let args = if args.len() > 1 {
                    args[1..].join(" ")
                } else {
                    "".to_string()
                };
                let command = command.replace("$", "");
                let command = command.to_lowercase();
                match command.as_str() {
                    "say" => {
                        self.chat_post(&args);
                    }
                    "send" => {
                        self.send_to_chat(&args, connections);
                    }
                    "rainbow" => {
                        let size = "12";
                        let rainbowed = Rainbow::rainbow_text(&args, size);
                        self.chat_post(&rainbowed);
                    }
                    _ => {

                        self.chat_post("Unknown command");
                    }

                }



            }
        }
    }


}


struct Bakery{
    connections: Vec<Chat>,

}

impl Bakery{

    fn oven(username: &str, password: &str, room_list: Vec<&str>) -> Self {
        let mut bakery = Bakery {
            connections: vec![],
        };
        for i in room_list{
            let chat = Chat::new(i.to_string(), username.to_string(), password.to_string(), "chat");

            bakery.connections.insert(bakery.connections.len(), chat);
        };
        let chat = Chat::new("_pm".to_string(), username.to_string(), password.to_string(), "pm");
        bakery.connections.insert(bakery.connections.len(), chat);
        bakery.breadbun();
        bakery

    }

    fn breadbun(&mut self){
        let anpan_is_tasty = true;
        while anpan_is_tasty {
            for con in &mut self.connections{
                let mut buf = [0; 1024];
                if let Ok(len) = con.cumsock.read(&mut buf) {
                    if len > 0 {
                        let data = &buf[..len];
                        for x in data.split(|b| b == &0x00) {
                            let s = String::from_utf8_lossy(x);
                            let s = s.trim();
                            let s = s.split(":");
                            let collection = s.collect::<Vec<&str>>();
                            con.events(collection, self.connections);
                        }
                    }
                }

            }
        }
    }
}

fn main() {
    Bakery::oven("anpanbot", "", vec!["","", ""]);


}

And the error code:

error[E0596]: cannot borrow `i` as mutable, as it is not declared as mutable
   --> src/main.rs:182:16
    |
182 |                i.chat_post(&message);
    |                ^ cannot borrow as mutable
    |
help: consider changing this to be mutable
    |
180 |        for mut i in connections{
    |            +++

warning: variable does not need to be mutable
   --> src/main.rs:219:13
    |
219 |         let mut message = Message{
    |             ----^^^^^^^
    |             |
    |             help: remove this `mut`
    |
    = note: `#[warn(unused_mut)]` on by default

error[E0505]: cannot move out of `self.connections` because it is borrowed
   --> src/main.rs:317:52
    |
304 |     fn breadbun(&mut self){
    |                 --------- binding `self` declared here
...
307 |             for con in &mut self.connections{
    |                        --------------------- borrow of `self.connections` occurs here
...
317 |                             con.events(collection, self.connections);
    |                                                    ^^^^^^^^^^^^^^^^ move out of `self.connections` occurs here

error[E0507]: cannot move out of `self.connections` which is behind a mutable reference
   --> src/main.rs:317:52
    |
317 | ...                   con.events(collection, self.connections);
    |                                              ^^^^^^^^^^^^^^^^ move occurs because `self.connections` has type `Vec<Chat>`, which does not implement the `Copy` trait

Some errors have detailed explanations: E0505, E0507, E0596.
For more information about an error, try `rustc --explain E0505`.
warning: `chatangorust` (bin "chatangorust") generated 2 warnings
error: could not compile `chatangorust` (bin "chatangorust") due to 3 previous errors; 2 warnings emitted

i have posted a more clear example in the reply to jumpnbrownweasel

Here are a couple things to know.

  • The connections: Vec<Chat> param is an owned value, so it is consumed by this function. That may what you want, but I thought I should point it out since it is a little unusual that you are modifying these elements while consuming them.
  • To modify the elements of connections you need to do two things:
    • change the param to mut connections: Vec<Chat> as suggested in the errror
    • iterate over mutable elements using for i in &mut connections {

After applying those fixes, I am still getting the error:


error[E0505]: cannot move out of `self.connections` because it is borrowed
   --> src/main.rs:317:52
    |
304 |     fn breadbun(&mut self){
    |                 --------- binding `self` declared here
...
307 |             for con in &mut self.connections{
    |                        --------------------- borrow of `self.connections` occurs here
...
317 |                             con.events(collection, self.connections);
    |                                                    ^^^^^^^^^^^^^^^^ move out of `self.connections` occurs here

error[E0507]: cannot move out of `self.connections` which is behind a mutable reference
   --> src/main.rs:317:52
    |
317 | ...                   con.events(collection, self.connections);
    |                                              ^^^^^^^^^^^^^^^^ move occurs because `self.connections` has type `Vec<Chat>`, which does not implement the `Copy` trait

I just do not see any other way to accomplish send_to_chat

don't take the parameter connection by value, which moves the argument into the function, since you need mut access, use a mut reference instead, but better yet, you can use a mut slice, and a reference to Vec can be automatically coerced to a slice reference:

fn on_post(&mut self, message: Message, connections: &mut [Chat]) { ... }

same for all other methods with an connection parameter.

don't iterate the Vec by value, use a borrowed iterator instead: for shared access, use .iter(), for exclusive access, use .iter_mut():

for i in connections.iter_mut() {
    if room.to_string() == i.name() {
        i.chat_post(&message);
    }
}

I suggest you read the chapter about ownership in the book:

Sorry, I didn't look at that error.

Here you have the self.connections vector borrowed and you're processing one element of it. While doing this, you're passing the entire vector (attempting to move/consume it) to another function. This doesn't make logical sense, and Rust won't let you do it. Somehow you need to do these mutations separately, which may require some rethinking.

Note that you can't move a field (self.connections in this case) out of a structure, unless you're discarding that entire structure as well. You could pass a &mut reference instead of passing by value, but you will still have a borrow error because you can't have two active &mut references to a value at the same time. Again, you need to try to untangle this and rethink it.

it still gives me a borrow error on con.events(collection, &mut self.connections);
perhaps what i am trying to do with this structure that i have made is just something that rust will not allow? I will check out the rust book. i have mainly been learning from video tutorials.

I tried to code it similarly to a ruby bot that i made. I am guessing you are saying that the code needs to be entirely redone for it to be something more "rusty".

the problem with your design is there's no clear ownership relationship. a simple workaround is to rewrite this loop with splitted slices. but this is a just a workaround, you really need to redesign a architecture with clear layers of abstraction.

here's the split slice workaround:

for i in 0..self.connections.len() {
    let (par0, part1) = self.connections.split_at_mut(i);
    let (conn, part1) = part1.split_first_mut().unwrap();
    let mut buf = [0; 1024];
    if let Ok(len) = conn.cumsock.read(&mut buf) {
        //...
        conn.events(collections, part0);
        conn.events(collections, part1);
    }
}

I think my answer then is to study up more and redo it all. i have only been learning rust for about a week now.

not bad for just a week.

when I do this kind of many to many broadcast, my usual approach is to split the reader and writer into separate thread, and put a queue/channel in between. note, although the sockets are thread safe, some synchronization might be needed for other data.

another common approach is an "agent"-like structure, where each client is handled by a dedicated agent (i.e. a thread or an async task), and messages are sent using mpsc channels, both for single-cast and multi-cast messages. this approach is easier to implement, but if there are large number of clients, async tasks are preferred over vanilla threads, to reduce overhead.

do not use Vec<...>, please use & Vec<...>, a func usually do not want a parameter's ownership

This borrowing inspection has taught you a good lesson.

The problem with this approach is that Rust's ownership and borrowing rules are really different than most other languages, particularly languages with garbage collection like Ruby. Implementation and design take more time, because Rust has stricter constraints. However, the end result is often a better design and implementation.

That's why @nerditation made a good suggestion -- to study the ownership chapter of Rust. I suggest studying the book for a while, and then coming back to this problem and try a new approach based on how Rust works.

i managed to fix it using a couple different cloning methods, on the individual sockets for the Chat struct, and cloning the bakery struct using Arc. it is now fully functional but i still think it might need to be completely redone.

/*
 * TODO: CLEAN UP CODE AND ADD EVENTS.
 */


use std::net::TcpStream;
use std::io::prelude::*;
use rand::Rng;
use std::thread;
use std::time::Duration;
use regex::Regex;
use std::collections::HashMap;
use reqwest::header::USER_AGENT;
use reqwest::header::HeaderValue;
use html_escape::encode_text;
mod rainbow;
use rainbow::Rainbow; //found in extra-stuff repository. i do not own this code.
use std::sync::{Arc, Mutex};



fn g_server(mut group: String) -> String{

    let weights = [[5, 75],[6, 75],[7, 75],[8, 75],[16, 75],[17, 75],[18, 75],[9, 95],[11, 95],[12, 95],[13, 95],[14, 95],[15, 95],[19, 110],[23, 110],[24, 110],[25, 110],[26, 110],[28, 104],[29, 104],[30, 104],[31, 104],[32, 104],[33, 104],[35, 101],[36, 101],[37, 101],[38, 101],[39, 101],[40, 101],[41, 101],[42, 101],[43, 101],[44, 101],[45, 101],[46, 101],[47, 101],[48, 101],[49, 101],[50, 101],[52, 110],[53, 110],[55, 110],[57, 110],[58, 110],[59, 110],[60, 110],[61, 110],[62, 110],[63, 110],[64, 110],[65, 110],[66, 110],[68, 95],[71, 116],[72, 116],[73, 116],[74, 116],[75, 116],[76, 116],[77, 116],[78, 116],[79, 116],[80, 116],[81, 116],[82, 116],[83, 116],[84, 116]];

    group = group.replace("-","q").replace("_","q");
    let a = if group.len() > 6 {
        let a = if group.len() >= 9 {
            &group[6..9]
        } else {
            &group[6..]
        };
        let a = i64::from_str_radix(a, 36).unwrap() as f64;
        let a = f64::max(1000.0, a);
        a
    }
    else{
        1000.0
    };
    let b = std::cmp::min(5, group.len());
    let b = &group[..b];
    let b = i64::from_str_radix(b, 36).unwrap() as f64;
    let num = (b / a) % 1.0;
    let mut anpan = 0.0;
    let mut s_number = 0;
    let total_weight: f64 = weights.iter().map(|a| a[1] as f64).sum();
    for x in weights {
        anpan += x[1] as f64 / total_weight;
        if num <= anpan {
            s_number += x[0];
            break;
        }
    }

    format!("s{}.chatango.com:443", s_number)
}

fn auth(user: &str, pass: &str) -> String {
    let mut form = HashMap::new();
    form.insert("user_id", user);
    form.insert("password", pass);
    form.insert("storecookie", "on");
    form.insert("checkerrors", "yes");
    let client = reqwest::blocking::Client::new();
    let res = client.post("http://chatango.com/login")
    .form(&form)
    .header(USER_AGENT, HeaderValue::from_static(
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 \
(KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
    ))
    .send();

    let res = res.unwrap();
    let res = res.headers();
    let cookie: Vec<_> = res.get_all("set-cookie").iter().filter_map(|val| val.to_str().ok()).map(|s| s.to_string()).collect();
    let cookie = &cookie[2];
    let re = Regex::new(r"auth.chatango.com=(.*?);").unwrap();
    let extract = re.captures(cookie).unwrap().get(1);
    let extract = extract.unwrap().as_str().to_string();
    extract
}


struct Message{
    user: String,
    cid: String,
    uid: String,
    time: String,
    sid: String,
    ip: String,
    content: String,
    chat: String,
}

struct Chat{
    name: String,
    cumsock: TcpStream,
    wbyte: String,
    byteready: bool,
    font_color: String,
    name_color: String,
    font_size: i32,
    mgr: Option<Arc<Mutex<Bakery>>>,
    username: String,
    password: String,
}

impl Clone for Chat {
    fn clone(&self) -> Self {
        Self {
            name: self.name.clone(),
            cumsock: self.cumsock.try_clone().expect("Failed to clone TcpStream"),
            wbyte: self.wbyte.clone(),
            byteready: self.byteready,
            font_color: self.font_color.clone(),
            name_color: self.name_color.clone(),
            font_size: self.font_size,
            mgr: self.mgr.clone(),
            username: self.username.clone(),
            password: self.password.clone(),
        }
    }
}

impl Chat{
    fn new(name: String, username: String, password: String, ctype: &str) -> Self {
        let server = if ctype == "chat" {
            g_server(name.clone())
        } else {
            let server = "c1.chatango.com:5222".to_string();
            server
        };
        let mut chat = Chat{
            name: name,
            cumsock: TcpStream::connect(server).unwrap(),
            wbyte: "".to_string(),
            byteready: false,
            mgr: None,
            name_color: "C7A793".to_string(),
            font_color: "F7DCCE".to_string(),
            font_size: 10,
            username,
            password,
        };

        chat.cumsock.set_nonblocking(true).expect("set_nonblocking call failed");
        if ctype == "chat" {
            chat.chat_login();
        } else {
            chat.pm_login();
        };
        chat
    }

    fn chat_login(&mut self){

        let chat_id = rand::thread_rng().gen_range(10_u128.pow(15)..10_u128.pow(16)).to_string();
        self.chat_send(vec!["bauth", &self.name.clone(), &chat_id, &self.username.clone(), &self.password.clone()]);
        self.byteready = true;
        let mut socket_clone = self.cumsock.try_clone().expect("Failed to clone socket");
        thread::spawn(move || {
            loop {
                let data = b"\r\n\x00";
                socket_clone.write(data);
                thread::sleep(Duration::from_secs(20));

            }
        });

    }

    fn pm_login(&mut self){

        let auth = auth(&self.username.clone(), &self.password.clone());
        let to_send = format!("tlogin:{}:2\x00", auth);
        let _ = self.cumsock.write(to_send.as_bytes()).unwrap();
        let mut socket_clone = self.cumsock.try_clone().expect("Failed to clone socket");
        thread::spawn(move || {
            loop {
                thread::sleep(Duration::from_secs(20));
                let data = b"\r\n\x00";
                socket_clone.write(data);
            }
        });

    }

    fn chat_join(&mut self, args: &str) {
        let chat = Chat::new(args.to_string(), self.username.clone(), self.password.clone(), "chat");
        let bakery = self.mgr.as_ref().unwrap().clone();
        let mut bakery =  bakery.lock().unwrap();
        bakery.connections.push(chat);
    }

    fn chat_send(&mut self, data: Vec<&str>){
        let ending = if self.byteready {
            "\r\n\x00"
        } else{
            "\x00"
        };
        let data = data.join(":");
        let data = format!("{}{}", data, ending);

        self.cumsock.write(data.as_bytes());

    }

    fn chat_post(&mut self, args: &str){
        let message  = format!("<n{}/><f x{}{}=\"0\">{}</f>\r\n\x00", &self.name_color, &self.font_size, &self.font_color,  args);
        self.chat_send(vec!["bm","derp", "2048", &message]);

    }

   fn send_to_chat(&mut self, room: &str, args: &str){
        println!("derp1");
        let bakery = self.mgr.as_ref().unwrap().clone();
        let mut bakery =  bakery.lock().unwrap();
        println!("derp2");
        for i in &mut bakery.connections{
            if &i.name == room {
                i.chat_post(&args);
                self.chat_post("Done.");
            };
        }


    }

    fn events(&mut self, collection: Vec<&str>){
        let event = &collection[0];
        let data = &collection[1..];
        //println!("event: {:?} data: {:?}", event, data);
        if *event == "b"{
            self.event_b(data);
        }
        if *event == "inited"{
            self.event_inited(data);
        }
    }

    fn event_b(&mut self, data: &[&str]){
        let user = data[1];
        let alias = data[2];
        let user = if user == "" {
            "None"
        } else{
            user
        };
        let user = if user == "None"{
            if alias == ""{
                "None"
            } else if alias == "None"{
                "None"
            } else{
                alias
            }
        }else
        { user};
        let re = Regex::new(r"<.*?>").unwrap();
        let content = data[9..].join("");
        let content = re.replace_all(&content, "");
        let content = html_escape::decode_html_entities(&content);
        let mut message = Message{
            user: user.to_string(),
            cid: data[4].to_string(),
            uid: data[3].to_string(),
            time: data[0].to_string(),
            sid: data[5].to_string(),
            ip: data[6].to_string(),
            content: content.to_string(),
            chat: self.name.clone(),
        };

        self.on_post(message);


    }

    fn event_inited(&mut self, data: &[&str]){
        self.chat_send(vec!["getpremium", "1"]);
        self.chat_send(vec!["g_participants", "start"]);
        self.chat_send(vec!["getbannedwords"]);
        self.chat_send(vec!["msgbg", "1"]);
        println!("logged into: {}", &self.name);
    }


    fn on_post(&mut self, message: Message){
        //println!("{}: {}", message.user, message.content);
        if message.content.to_lowercase().contains("herenti"){
            println!("{}: {}: {}", message.user, message.chat, message.content)
        }
        if message.chat != "".to_string(){
            if message.content.starts_with("$") {
                let args = message.content.split(" ");
                let args: Vec<&str> = args.collect();
                let command = args[0];
                let args = if args.len() > 1 {
                    args[1..].join(" ")
                } else {
                    "".to_string()
                };
                let command = command.replace("$", "");
                let command = command.to_lowercase();
                self.commands(message, &command, &args);

            }
        }
    }

    fn commands(&mut self, message: Message, command: &str, args: &str){
        let mods = vec!["", "", "", ""];
        match command {
            "say" => {
                self.chat_post(&args);
            }
            "rainbow" => {
                let size = "12";
                let rainbowed = Rainbow::rainbow_text(&args, size);
                self.chat_post(&rainbowed);
            }

            "send" => {
                let user = message.user.as_str();
                if mods.contains(&user){
                    let args = args.split(" ");
                    let args = args.collect::<Vec<&str>>();
                    let room = args[0];
                    let message = args[1..].join(" ");
                    self.send_to_chat(room, &message);
                } else {
                    self.chat_post("You do not have permission to use this command.");
                }
            }
            "join" => {
                let user = message.user.as_str();
                if mods.contains(&user){
                    self.chat_join(&args);
                    self.chat_post("Done.");
                } else {
                    self.chat_post("You do not have permission to use this command.");
                }
            }
            "rsend" => {
                let user = message.user.as_str();
                if mods.contains(&user){
                    let args = args.split(" ");
                    let args = args.collect::<Vec<&str>>();
                    let room = args[0];
                    let message = args[1..].join(" ");
                    let size = "12";
                    let rainbowed = Rainbow::rainbow_text(&message, size);
                    self.send_to_chat(room, &rainbowed);
                } else {
                    self.chat_post("You do not have permission to use this command.");
                }
                }

            _ => {

                self.chat_post("Unknown command");
            }

        }
    }


}


struct Bakery{
    connections: Vec<Chat>,

}

impl Bakery{

    fn oven(username: &str, password: &str, room_list: Vec<&str>) -> Self {
        let mut bakery = Bakery {
            connections: vec![],
        };
        for i in room_list{
            let chat = Chat::new(i.to_string(), username.to_string(), password.to_string(), "chat");

            bakery.connections.push(chat);
        };
        let chat = Chat::new("_pm".to_string(), username.to_string(), password.to_string(), "pm");
        bakery.connections.push(chat);




        bakery

    }


}

fn main() {

    let bakery = Arc::new(Mutex::new(Bakery::oven("", "", vec!["", "", ""])));
    {
        let mut clone_bakery = bakery.lock().unwrap();
        for i in &mut clone_bakery.connections {
            i.mgr = Some(Arc::clone(&bakery));
        }
    }
    breadbun(Arc::clone(&bakery));

    fn breadbun(bakery: Arc<Mutex<Bakery>>) {
        let anpan_is_tasty = true;
        while anpan_is_tasty {
            let mut conns_clone = {
                let bakery = bakery.lock().unwrap();
                bakery.connections.clone()
            };
            for con in &mut conns_clone {
                let mut buf = [0; 1024];
                if let Ok(len) = con.cumsock.read(&mut buf) {
                    if len > 0 {
                        let data = &buf[..len];
                        for x in data.split(|b| b == &0x00) {
                            let s = String::from_utf8_lossy(x);
                            let s = s.trim();
                            let s = s.split(":");
                            let collection = s.collect::<Vec<&str>>();
                            con.events(collection);
                        }
                    }
                }

            }
        }
    }


}

congrats! you are making good progress.

your design has a very strong feel of OOP paradigm, notably, the back pointer mgr: Arc<Mutex<Bakery>>, and this is very understandable for someone with a ruby background.

but this is problematic, because you created cyclic reference counters, which prevents the data being dropped, leading to resource leaks. circular references is the normal in languages with GC, but should be avoided in a language using ownership based resource management.

I think one major issue with your design is you conflated message transportation (send and receive) with message routing (one-to-one, one-to-many, many-to-many).

my suggestion for the next iteration: the Chat type should only process "one-to-one" messages involving self and a peer, extract all the multiple clients logic into the Bakery type or a new type.

btw, a trick to get out of the OOP mindset, is that you should try hard to refrain yourself from the urge of accessing all the states through self. don't be afraid to use too many parameters for functions, this could force you to get a better feel of the language.

1 Like

i was able to move the multiple stream stuff all into the bakery quite easily, now i dont have to use arc. i am curious if there are still cyclic references:

/*

*/


use std::net::TcpStream;
use std::io::prelude::*;
use rand::Rng;
use std::thread;
use std::time::Duration;
use regex::Regex;
use std::collections::HashMap;
use reqwest::header::USER_AGENT;
use reqwest::header::HeaderValue;
use html_escape::encode_text;
mod rainbow;
use rainbow::Rainbow; //found in extra-stuff repository. i do not own this code.
use std::sync::{Arc, Mutex};
use serde_json;



fn g_server(mut group: String) -> String{

    let weights = [[5, 75],[6, 75],[7, 75],[8, 75],[16, 75],[17, 75],[18, 75],[9, 95],[11, 95],[12, 95],[13, 95],[14, 95],[15, 95],[19, 110],[23, 110],[24, 110],[25, 110],[26, 110],[28, 104],[29, 104],[30, 104],[31, 104],[32, 104],[33, 104],[35, 101],[36, 101],[37, 101],[38, 101],[39, 101],[40, 101],[41, 101],[42, 101],[43, 101],[44, 101],[45, 101],[46, 101],[47, 101],[48, 101],[49, 101],[50, 101],[52, 110],[53, 110],[55, 110],[57, 110],[58, 110],[59, 110],[60, 110],[61, 110],[62, 110],[63, 110],[64, 110],[65, 110],[66, 110],[68, 95],[71, 116],[72, 116],[73, 116],[74, 116],[75, 116],[76, 116],[77, 116],[78, 116],[79, 116],[80, 116],[81, 116],[82, 116],[83, 116],[84, 116]];

    group = group.replace("-","q").replace("_","q");
    let a = if group.len() > 6 {
        let a = if group.len() >= 9 {
            &group[6..9]
        } else {
            &group[6..]
        };
        let a = i64::from_str_radix(a, 36).unwrap() as f64;
        let a = f64::max(1000.0, a);
        a
    }
    else{
        1000.0
    };
    let b = std::cmp::min(5, group.len());
    let b = &group[..b];
    let b = i64::from_str_radix(b, 36).unwrap() as f64;
    let num = (b / a) % 1.0;
    let mut anpan = 0.0;
    let mut s_number = 0;
    let total_weight: f64 = weights.iter().map(|a| a[1] as f64).sum();
    for x in weights {
        anpan += x[1] as f64 / total_weight;
        if num <= anpan {
            s_number += x[0];
            break;
        }
    }

    format!("s{}.chatango.com:443", s_number)
}

fn auth(user: &str, pass: &str) -> String {
    let mut form = HashMap::new();
    form.insert("user_id", user);
    form.insert("password", pass);
    form.insert("storecookie", "on");
    form.insert("checkerrors", "yes");
    let client = reqwest::blocking::Client::new();
    let res = client.post("http://chatango.com/login")
    .form(&form)
    .header(USER_AGENT, HeaderValue::from_static(
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 \
(KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
    ))
    .send();

    let res = res.unwrap();
    let res = res.headers();
    let cookie: Vec<_> = res.get_all("set-cookie").iter().filter_map(|val| val.to_str().ok()).map(|s| s.to_string()).collect();
    let cookie = &cookie[2];
    let re = Regex::new(r"auth.chatango.com=(.*?);").unwrap();
    let extract = re.captures(cookie).unwrap().get(1);
    let extract = extract.unwrap().as_str().to_string();
    extract
}

fn youtube(search: &str) -> String {
    let url = format!("https://www.googleapis.com/youtube/v3/search?q={}&key=&type=video&maxResults=1&part=snippet", search);
    let res = reqwest::blocking::get(url).expect("REASON").text().unwrap();
    let data: serde_json::Value = serde_json::from_str(&res).unwrap();
    let result = if data["items"][0]["id"]["videoId"].as_str().is_some(){
        let _id = &data["items"][0]["id"]["videoId"].as_str().unwrap();
        let _title = &data["items"][0]["snippet"]["title"].as_str().unwrap();
        format!("https://www.youtube.com/watch?v={}\r\r\r\rVideo title [<b>{}</b>]", _id, _title)
    } else {
        "No video found.".to_string()
    };
    result


}


struct Message{
    user: String,
    cid: String,
    uid: String,
    time: String,
    sid: String,
    ip: String,
    content: String,
    chat: String,
}

struct Chat{
    name: String,
    cumsock: TcpStream,
    wbyte: String,
    byteready: bool,
    username: String,
    password: String,
}

impl Chat{
    fn new(name: String, username: String, password: String, ctype: &str) -> Self {
        let server = if ctype == "chat" {
            g_server(name.clone())
        } else {
            let server = "c1.chatango.com:5222".to_string();
            server
        };
        let mut chat = Chat{
            name: name,
            cumsock: TcpStream::connect(server).unwrap(),
            wbyte: "".to_string(),
            byteready: false,
            username,
            password,
        };

        chat.cumsock.set_nonblocking(true).expect("set_nonblocking call failed");
        if ctype == "chat" {
            chat.chat_login();
        } else {
            chat.pm_login();
        };
        chat
    }

    fn chat_login(&mut self){

        let chat_id = rand::rng().random_range(10_u128.pow(15)..10_u128.pow(16)).to_string();
        self.chat_send(vec!["bauth", &self.name.clone(), &chat_id, &self.username.clone(), &self.password.clone()]);
        self.byteready = true;
        let mut socket_clone = self.cumsock.try_clone().expect("Failed to clone socket");
        thread::spawn(move || {
            loop {
                let data = b"\r\n\x00";
                socket_clone.write(data);
                thread::sleep(Duration::from_secs(20));

            }
        });

    }

    fn pm_login(&mut self){

        let auth = auth(&self.username.clone(), &self.password.clone());
        let to_send = format!("tlogin:{}:2\x00", auth);
        let _ = self.cumsock.write(to_send.as_bytes()).unwrap();
        let mut socket_clone = self.cumsock.try_clone().expect("Failed to clone socket");
        thread::spawn(move || {
            loop {
                thread::sleep(Duration::from_secs(20));
                let data = b"\r\n\x00";
                socket_clone.write(data);
            }
        });

    }


    fn chat_send(&mut self, data: Vec<&str>){
        let ending = if self.byteready {
            "\r\n\x00"
        } else{
            "\x00"
        };
        let data = data.join(":");
        let data = format!("{}{}", data, ending);

        self.cumsock.write(data.as_bytes());

    }




}


struct Bakery{
    connections: Vec<Chat>,
    current_chat: String,
    to_send_room: String,
    username: String,
    password: String,
    font_color: String,
    name_color: String,
    font_size: i32,

}

impl Bakery{

    fn oven(username: &str, password: &str, room_list: Vec<&str>) -> Self {
        let mut bakery = Bakery {
            connections: vec![],
            current_chat: "None".to_string(),
            to_send_room: "None".to_string(),
            username: username.to_string(),
            password: password.to_string(),
            name_color: "C7A793".to_string(),
            font_color: "F7DCCE".to_string(),
            font_size: 10,
        };
        for i in room_list{
            let chat = Chat::new(i.to_string(), username.to_string(), password.to_string(), "chat");

            bakery.connections.push(chat);
        };
        let chat = Chat::new("_pm".to_string(), username.to_string(), password.to_string(), "pm");
        bakery.connections.push(chat);




        bakery

    }

    fn chat_post(&mut self, args: &str){
        let room = if self.to_send_room != "None".to_string(){
            &self.to_send_room
        }
        else {
            &self.current_chat
        };
        let message  = format!("<n{}/><f x{}{}=\"0\">{}</f>\r\n\x00", &self.name_color, &self.font_size, &self.font_color,  args);
        for i in &mut self.connections {
            if i.name == room.to_string() {
                i.chat_send(vec!["bm","derp", "2048", &message]);
            }
        }

    }

    fn send_to_chat(&mut self, room: &str, args: &str){
        for i in &mut self.connections{
            if &i.name == room {
                self.to_send_room = room.to_string();
            };
        };
        if self.to_send_room != "None".to_string(){
            self.chat_post(&args);
            self.to_send_room = "None".to_string();
            self.chat_post("Done.");
        }
        else {
            self.chat_post("I am not in that chat.")
        }


    }

    fn chat_join(&mut self, args: &str) {
        let chat = Chat::new(args.to_string(), self.username.clone(), self.password.clone(), "chat");
        self.connections.push(chat);
    }

    fn chat_send(&mut self, data: Vec<&str>){
        for i in &mut self.connections {
            if i.name == self.current_chat {
                i.chat_send(data.clone());
            }
        }

    }

    fn events(&mut self, chatname: &str, collection: Vec<&str>){
        let event = &collection[0];
        let data = &collection[1..];
        self.current_chat = chatname.to_string();
        //println!("event: {:?} data: {:?}", event, data);
        if *event == "b"{
            self.event_b(data);
        }
        if *event == "inited"{
            self.event_inited(data);
        }
    }

    fn event_b(&mut self, data: &[&str]){
        let user = data[1];
        let alias = data[2];
        let user = if user == "" {
            "None"
        } else{
            user
        };
        let user = if user == "None"{
            if alias == ""{
                "None"
            } else if alias == "None"{
                "None"
            } else{
                alias
            }
        }else
        { user};
        let re = Regex::new(r"<.*?>").unwrap();
        let content = data[9..].join("");
        let content = re.replace_all(&content, "");
        let content = html_escape::decode_html_entities(&content);
        let mut message = Message{
            user: user.to_string(),
            cid: data[4].to_string(),
            uid: data[3].to_string(),
            time: data[0].to_string(),
            sid: data[5].to_string(),
            ip: data[6].to_string(),
            content: content.to_string(),
            chat: self.current_chat.clone(),
        };

        self.on_post(message);


    }

    fn event_inited(&mut self, data: &[&str]){
        self.chat_send(vec!["getpremium", "1"]);
        self.chat_send(vec!["g_participants", "start"]);
        self.chat_send(vec!["getbannedwords"]);
        self.chat_send(vec!["msgbg", "1"]);
        println!("logged into: {}", &self.current_chat);
    }


    fn on_post(&mut self, message: Message){
        //println!("{}: {}", message.user, message.content);
        if message.content.to_lowercase().contains("herenti"){
            println!("{}: {}: {}", message.user, message.chat, message.content)
        }
        if message.chat != "".to_string(){
            if message.content.starts_with("$") {
                let args = message.content.split(" ");
                let args: Vec<&str> = args.collect();
                let command = args[0];
                let args = if args.len() > 1 {
                    args[1..].join(" ")
                } else {
                    "".to_string()
                };
                let command = command.replace("$", "");
                let command = command.to_lowercase();
                self.commands(message, &command, &args);

            }
        }
    }

    fn commands(&mut self, message: Message, command: &str, args: &str){
        let mods = vec![""];
        let user = message.user.as_str();
        let ismod = if mods.contains(&user) {
            true
        } else {
            false
        };
        match command {
            "say" => {
                self.chat_post(&args);
            }
            "yt" => {
                self.chat_post(&youtube(&args));
            }
            "rainbow" => {
                let size = "12";
                let rainbowed = Rainbow::rainbow_text(&args, size);
                if rainbowed.len() > 2490{
                    self.chat_post("That message is too long for chatango.");
                } else{
                    self.chat_post(&rainbowed);
                }
            }

            "send" => {
                if ismod {
                    let args = args.split(" ");
                    let args = args.collect::<Vec<&str>>();
                    let room = args[0];
                    let message = args[1..].join(" ");
                    self.send_to_chat(room, &message);
                } else {
                    self.chat_post("You do not have permission to use this command.");
                }
            }
            "join" => {
                if ismod {
                    self.chat_join(&args);
                    self.chat_post("Done.");
                } else {
                    self.chat_post("You do not have permission to use this command.");
                }
            }
            "rsend" => {
                if ismod {
                    let args = args.split(" ");
                    let args = args.collect::<Vec<&str>>();
                    let room = args[0];
                    let message = args[1..].join(" ");
                    let size = "12";
                    let rainbowed = Rainbow::rainbow_text(&message, size);
                    if rainbowed.len() > 2490{
                        self.chat_post("That message is too long for chatango.");
                    } else{
                        self.send_to_chat(room, &rainbowed);
                    }
                } else {
                    self.chat_post("You do not have permission to use this command.");
                }
            }

            _ => {

                self.chat_post("Unknown command");
            }

        }
    }


}

fn main() {

    let mut bakery = Bakery::oven("", "", vec![""]);

    breadbun(&mut bakery);

    fn breadbun(bakery: &mut Bakery) {
        let anpan_is_tasty = true;
        while anpan_is_tasty {
            let mut cloned_conn = vec![];
            for i in &mut bakery.connections{
                cloned_conn.push((i.name.clone(), i.cumsock.try_clone().expect("failed to clone socket.")));
            };
            for (name, mut con) in cloned_conn {
                let mut buf = [0; 1024];
                if let Ok(len) = con.read(&mut buf) {
                    if len > 0 {
                        let data = &buf[..len];
                        for x in data.split(|b| b == &0x00) {
                            let s = String::from_utf8_lossy(x);
                            let s = s.trim();
                            let s = s.split(":");
                            let collection = s.collect::<Vec<&str>>();
                            bakery.events(&name.clone(), collection);
                        }
                    }
                }

            }
        }
    }


}