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

⚓ rust    📅 2025-05-21    👤 surdeus    👁️ 3      

surdeus

Warning

This post was published 40 days ago. The information described in this article may have changed.

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![""]);


}

3 posts - 3 participants

Read full topic

🏷️ rust_feed