Hello everyone, you gonna hate me but i use ChatGPT as a tutor in understanding Rust. Its a study that i plan to write about. As a first program, with Chat's help im trying to make Discord bot for users to tip imaginary tokens to each other. Here is the tip function that cannot be sent between threads safely:
use serenity::{
framework::standard::{
Args, CommandResult,
macros::{command, group},
},
model::{channel::Message,}
};
use serenity::prelude::Context;
use super::database::{insert_row, get_user, update_balance, get_balance, update_address};
use lazy_static::lazy_static;
use r2d2_sqlite::SqliteConnectionManager;
use std::future::*;
use std::sync::{Arc, Mutex};
use rusqlite;
// Set up the connection manager and pool
lazy_static! {
static ref POOL: Arc<Mutex<r2d2::Pool<SqliteConnectionManager>>> = {
let manager = SqliteConnectionManager::file("userstable.db");
let pool = r2d2::Pool::builder()
.build(manager)
.unwrap_or_else(|_| panic!("Error creating pool"));
Arc::new(Mutex::new(pool))
};
}
#[group]
#[commands(tip, update, balance, register)]
pub struct General;
#[command]
pub async fn tip(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
// Clone the connection pool and HTTP client
let pool = POOL.clone();
let http = ctx.http.clone();
// Parse the user ID or username and amount from the command arguments
let name = args.single::<String>()?;
let amount = args.single::<i32>()?;
// Look up the user ID of the recipient
let recipient_id = match name.parse::<u64>() {
Ok(id) => match http.get_user(id).await {
Ok(user) => user.id,
Err(_) => {
msg.channel_id.say(&http, "User not found").await;
return Err("User not found".into());
}
},
Err(_) => {
msg.channel_id.say(&http, "Invalid user ID or username").await;
return Err("Invalid user ID or username".into());
}
};
// Lock the connection pool and get a connection
let mutex_guard = pool.lock().expect("Error acquiring mutex");
let conn = mutex_guard.get()?;
// Ensure that the sender and recipient exist in the database
let sender_exists = get_user(&conn, *msg.author.id.as_u64()).await.is_ok();
let recipient_exists = get_user(&conn, *recipient_id.as_u64()).await.is_ok();
let sender_balance = get_balance(&conn, *msg.author.id.as_u64()).await;
// Update the balances of the sender and recipient in the database
update_balance(&conn, *msg.author.id.as_u64(), -amount).await?;
update_balance(&conn, *recipient_id.as_u64(), amount).await?;
// Send a message to the channel indicating that the tip was successful
let sender_name = msg.author.id.as_u64();
let recipient_name = recipient_id.as_u64();
msg.channel_id.say(&http, format!("{} tipped {} {}", sender_name, recipient_name, amount)).await;
Ok(())
}
and here is the error:
error: future cannot be sent between threads safely
--> src\modules\commands.rs:38:1
|
38 | #[command]
| ^^^^^^^^^^ future created by async block is not `Send`
|
= help: within `impl futures::Future<Output = Result<(), Box<dyn std::error::Error + Sync + std::marker::Send>>>`, the trait `std::marker::Send` is not implemented for `std::sync::MutexGuard<'_, Pool<SqliteConnectionManager>>`
note: future is not `Send` as this value is used across an await
--> src\modules\commands.rs:68:65
|
38 | #[command]
| - `mutex_guard` is later dropped here
...
64 | let mutex_guard = pool.lock().expect("Error acquiring mutex");
| ----------- has type `std::sync::MutexGuard<'_, Pool<SqliteConnectionManager>>` which is not `Send`
...
68 | let sender_exists = get_user(&conn, *msg.author.id.as_u64()).await.is_ok();
| ^^^^^^ await occurs here, with `mutex_guard` maybe used later
note: required by a bound in `serenity::FutureExt::boxed`
--> .cargo\registry\src\github.com-1ecc6299db9ec823\futures-util-0.3.25\src\future\future\mod.rs:520:23
|
520 | Self: Sized + Send + 'a,
| ^^^^ required by this bound in `serenity::FutureExt::boxed`
= note: this error originates in the attribute macro `command` (in Nightly builds, run with -Z macro-backtrace for more info)
I don't know what to do, I thought that creating Pool for sqlite and wrapping it in Arc and Mutex would help, but it didn't.