I recently began learning Tauri to build an interface for an AI project. However, I am running into issues when it comes to storing a game board as a state.
The struct I am trying to store is this one:
pub struct StrategicBoard {
pub subboards: Vec<Board>,
pub legal_boards: Vec<usize>,
pub current_board: Option<usize>,
pub wins: Board,
pub player: i8,
pub winner: i8,
pub in_play: bool,
checkpoint: Checkpoint,
rng: ThreadRng,
}
And the code I am using in my Tauri application is:
#![cfg_attr(
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
)]
// Modules that store the code for the game and the AI
pub mod monte_carlo;
pub mod sttt;
use std::sync::Mutex;
use sttt::StrategicBoard;
pub struct BoardState(pub Mutex<StrategicBoard>);
fn new_game(state: State<BoardState>) {
// Make a new board in here and store it in the state
}
fn main() {
tauri::Builder::default()
.run(tauri::generate_context!())
.manage(BoardState)
.expect("error while running tauri application");
}
However it runs into this error:
`Rc<UnsafeCell<ReseedingRng<rand_chacha::chacha::ChaCha12Core, OsRng>>>` cannot be sent between threads safely
Tauri won't allow me to store a ThreadRng in a state. But the AI needs to use random number generation to function. How can I get around this?
You don't have to store ThreadRng, you can always retrieve it with rand::thread_rng() in whatever function you need it. Doing so is cheap.
One thing of note: For games, it's not uncommon to have a deterministic rng, initialized by some seed value. You won't get that out of ThreadRng, it is seeded and regularly reseeded from OS random sources. If you do want a deterministic rng, you could use
ChaCha8Rng, which is Send and Sync, and can be stored in your struct.
Related
I am processing a lot of data. I am working with the cargo dumps, in hopes of creating interconnected graph of all crates. I get data from postgres dump, and try to connect it. I'm trying to connect each version of package, with latest possible version of dependency. I have to go through each dependency, find possible versions, parse version and see if the version of the package and required version of the dependency matches.
I can say that first found semver match is always the right one, since I get data ordered DESC from db. Now, to my problem. I programmed this logic, however then I found out that execution of my code is going to take a long time. Around 3 hours on my hardware, and a lot more in production. This is not acceptable. I've pinpointed my problem, being that I always create a new iterator and then proceed to consume it with using find method. Is there any way to find value inside a vec, without consuming the iterator?
I have the following structs:
#[derive(Debug, Clone, sqlx::FromRow)]
pub struct CargoCrateVersionRow {
pub id: i32,
pub crate_id: i32,
pub version_num: String,
pub created_at: sqlx::types::chrono::NaiveDateTime,
pub updated_at: sqlx::types::chrono::NaiveDateTime,
pub downloads: i32,
pub features: sqlx::types::Json<HashMap<String, Vec<String>>>,
pub yanked: bool,
pub license: Option<String>,
pub crate_size: Option<i32>,
pub published_by: Option<i32>,
}
#[derive(Debug, sqlx::FromRow)]
pub struct CargoDependenciesRow {
pub version_id: i32,
pub crate_id: i32,
pub req: String,
pub optional: bool,
pub features: Vec<String>,
pub kind: i32,
}
#[derive(Debug)]
pub struct CargoCrateVersionDependencyEdge {
pub version_id_from: i32,
pub version_id_to: i32,
pub optional: bool,
pub with_features: Vec<String>,
pub kind: i32,
}
SQL command for retrieving crate_dependencies:
select version_id, crate_id, req, optional, features, kind
from dependencies;
SQL command for retrieving crate_versions:
select
id, crate_id, num "version_num", created_at, updated_at,
downloads, features, yanked, license, crate_size, published_by
from
versions
order by
id desc;
My custom logic:
let mut cargo_crate_dependency_edges: Vec<CargoCrateVersionDependencyEdge> = vec![];
let mut i = 0;
for dep in crate_dependencies {
if i % 10_000 == 0 {
println!("done 10k");
}
let req = skip_fail!(VersionReq::parse(dep.req.as_str()));
// I do not wish to create new iter each time, as this includes millions of structs
let possible_crate_version = crate_versions.iter().find(|s| {
if s.crate_id != dep.crate_id {
return false;
}
// Here, I'm using semver crate
if let Ok(parsed_version) = Version::parse(&s.version_num) {
req.matches(&parsed_version)
} else {
false
}
});
if let Some(found_crate_version) = possible_crate_version {
cargo_crate_dependency_edges.push(
CargoCrateVersionDependencyEdge {
version_id_from: dep.version_id,
version_id_to: found_crate_version.id,
optional: dep.optional,
with_features: dep.features.clone(),
kind: dep.kind,
}
);
}
i += 1;
}
println!("{}", cargo_crate_dependency_edges.len());
After some time debugging, I've come to the conclusion that I need to somehow get rid of making the iterator, because that's my bottleneck. I've benchmarked the library, pushing onto the vec, basically everything.
A similar question I posted earlier is here
Rust can't modify RefCell in Rc, but completely different.
I want to simulate some natural process, so I have a Simulator, and a reactor like a NuclearReactor. The simulator will modify the reactor, and the reactor can reversely influance the simulator by modifying it. One important thing is that the NuclearReactor is wrapped from somewhere else, the solid_function must has a inmutable &self.
So after reading rust book of RefCell, I wrote something like these, It complies, but crashed.
use std::borrow::BorrowMut;
use std::cell::RefCell;
use std::rc::{Rc, Weak};
pub struct Simulator {
nr: NuclearReactor,
data: Vec<f64>,
}
impl Simulator {
pub fn on_nuclear_data(&mut self, x: i64) {
// modify self
}
pub fn run_simulation(&mut self) {
self.nr.write_simulator();
}
}
pub struct NuclearReactor {
simulator: Option<Weak<RefCell<Simulator>>>,
}
impl NuclearReactor {
pub fn solid_function(&self, x: i64) {
/*
this function `&self` is solid, so I have to use a RefCell to wrap Simulator
*/
}
pub fn write_simulator(&self) {
/*
thread 'main' panicked at 'already borrowed: BorrowMutError'
*/
(*self.simulator.as_ref().unwrap().upgrade().unwrap()).borrow_mut().on_nuclear_data(0);
}
}
pub fn main() {
let nr_ = NuclearReactor {
simulator: None
};
let mut sm_ = Rc::new(RefCell::new(Simulator {
nr: nr_,
data: vec![],
}));
(*sm_).borrow_mut().nr.simulator = Some(Rc::downgrade(&sm_));
(*sm_).borrow_mut().run_simulation();
}
Obviously, the runtime check of borrow_mut fails.
Actually the NuclearReactor is my online code, the Simulator is an offline test, so I wanna modify the NuclearReactor at a minimal cost to let it run on the offline environment. That's why I have to keep the function solid_function with an immutable &self. Changing it to a &mut self is and then move to-modifying objects to a seperate function is feasible, but then I have to modify the online code frequently at a high cost. It there anything cool that can solve it ?
Ok, after reading this: http://smallcultfollowing.com/babysteps/blog/2018/11/01/after-nll-interprocedural-conflicts/
I finnaly realized that what I am doing is something like below and rust was helping me avoiding bugs.
let v: Vec<i64> = vec![1,2,3];
for ele in v.iter_mut(){
v.push(1);
}
Thankfully, pushing NuclearReactor's modify to a temp buffer then apply them to Simulator is just enough to solve my problem.
Also, I didn't explain the question clearly (actually I didn't get the point to describe the question until I solved it), so the community can't help me.
For my first project I wanted to create a terminal implementation of Monopoly. I have created a Card, Player, and App structs. Originally my plan was to create an array inside the App struct holding a list of cards which I could then randomly select and run an execute() method on, which would push a log to the logs field of the App. What I thought would be best was this:
pub struct Card {
pub group: u8,
pub name: String,
pub id: u8,
}
impl Card {
pub fn new(group: u8, name: String, id: u8) -> Card {
Card { group, name, id }
}
pub fn execute(self, app: &mut App) {
match self.id {
1 => {
app.players[app.current_player].index = 24;
app.push_log("this works".to_string(), "LOG: ".to_string());
}
_ => {}
}
}
}
pub struct Player<'a> {
pub name: &'a str,
pub money: u64,
pub index: u8,
pub piece: char,
pub properties: Vec<u8>,
pub goojf: u8, // get out of jail freeh
}
impl<'a> Player<'a> {
pub fn new(name: &'a str, piece: char) -> Player<'a> {
Player {
name,
money: 1500,
index: 0,
piece,
properties: Vec::new(),
goojf: 0,
}
}
}
pub struct App<'a> {
pub cards: [Card; 1],
pub logs: Vec<(String, String)>,
pub players: Vec<Player<'a>>,
pub current_player: usize,
}
impl<'a> App<'a> {
pub fn new() -> App<'a> {
App {
cards: [Card::new(
11,
"Take a ride on the Penn. Railroad!".to_string(),
0,
)],
logs: vec![(
String::from("You've begun a game!"),
String::from("BEGIN!:"),
)],
players: vec![Player::new("Joe", '#')],
current_player: 0,
}
}
pub fn draw_card(&mut self) {
if self.players[self.current_player].index == 2
|| self.players[self.current_player].index == 17
|| self.players[self.current_player].index == 33
{
self.cards[0].execute(self);
}
}
pub fn push_log(&mut self, message: String, status: String) {
self.logs.push((message, status));
}
}
fn main() {}
However this code throws the following error:
error[E0507]: cannot move out of `self.cards[_]` which is behind a mutable reference
--> src/main.rs:76:13
|
76 | self.cards[0].execute(self);
| ^^^^^^^^^^^^^ move occurs because `self.cards[_]` has type `Card`, which does not implement the `Copy` trait
I managed to fix this error by simply declaring the array of cards in the method itself, however, this seem to be pretty brute and not at all efficient, especially since other methods in my program depend on cards. How could I just refer to a single array of cards for all of my methods implemented in App or elsewhere?
As others have pointed out, Card::execute() needs to take &self instead of self, but then you run into the borrow checker issue, which I'll spend the rest of this answer discussing.
It may seem odd, but Rust is actually protecting you here. The borrow checker does not look into functions to see what they do, so it has no idea that Card::execute() won't do something to invalidate the referenced passed as the first argument. For example, if App::cards was a vector instead of an array, it could clear the vector.
Something that could actually practically happen here to cause undefined behavior would be if Card::execute() took a string slice from self.name and then cleared the card's name attribute through the mutable reference to app. None of these actions would be prohibited, and you'd be left with an invalid reference to a string slice. This is why the borrow checker isn't letting you make this method call, and this is exactly the kind of accident that Rust is designed to prevent.
There's a few ways around this. One option is to only pass the pieces of the App value needed to complete the task. You can borrow different parts of the same value. The problem here is that the reborrow of self overlaps with the borrow self.cards[0]. Passing each field separately isn't very ergonomic though, as in this case you'll wind up having to pass a reference to pretty much everything else on App.
It looks like the Card values don't actually contain any game state, and are used as data for the game engine. If this is the case, then the cards can live outside of App, like so:
pub struct App<'a> {
// Changed to an unowned slice.
pub cards: &'a [Card],
pub logs: Vec<(String, String)>,
pub players: Vec<Player<'a>>,
pub current_player: usize,
}
impl<'a> App<'a> {
pub fn new(cards: &'a [Card]) -> App<'a> {
App {
cards,
// ...
Then in your main() you can initialize the data and borrow it:
fn main() {
let cards: [Card; 1] = [Card::new(
11,
"Take a ride on the Penn. Railroad!".to_string(),
0,
)];
let app = App::new(&cards);
}
This solves the compilation problem. I'd suggest making other changes, as well:
App should probably be renamed GameState or something to emphasize that this struct should contain only mutable game state, and no immutable reference data.
Player's name field should probably be an owned String instead of an unowned &str, otherwise some other entity in the program will need to own a string slice for the duration of the program so that Player can borrow it.
Does a card need to be able to mutate the cards in self? If you know that execute will not need access to cards, the easiest solution is to split App into further structs so you can better limit the access of the function.
Unless there is another layer to your program that interacts with App as a singular object, requiring everything be in a single struct to perform operations will likely only constrain your code. If possible it is better to split it into its core components so you can be more selective when sharing references and avoid needing to make so many fields pub.
Here is a rough example:
pub struct GameState<'a> {
pub players: Vec<Player<'a>>,
pub current_player: usize,
}
/// You may want to look into using https://crates.io/crates/log instead to make logging easier.
pub struct Logs {
entries: Vec<(String, String)>,
}
impl Logs {
/// Use ToOwned so you can use both both &str and String
pub fn push<S: ToOwned<String>>(&mut self, message: S, status: S) {
self.entries.push((message.to_owned(), status.to_owned()));
}
}
pub struct Card {
group: u8,
name: String,
id: u8,
}
impl Card {
pub fn new(group: u8, name: String, id: u8) -> Self {
Card { group, name, id }
}
/// Use &self because there is no reason we need are required to consume the card
pub fn execute(&self, state: &mut GameState, logs: &mut Logs) {
if self.id == 1{
state.players[state.current_player].index = 24;
logs.push("this works", "LOG");
}
}
}
Also as a side note, execute consumes a card when called since it does not take a reference. Since Card does not implement Copy, that would require it be removed from cards so it can be moved.
Misc Tips and Code Review
Using IDs
It looks like you frequently use IDs to distinguish between items. However, I think your code will look cleaner and be easier to write if you used more human readable types. For example, do cards need an ID? Usually it is preferable to define your struct based on how the data is used. Last time I played monopoly, I don't remember picking up a card and referring to the card ID to determine what to do. I would instead recommend defining a Card by how it is used in the came. If I am remembering correctly, each card contains a short message telling the player what to do. Technically a card could consist of just the message, but you can instead make your code a bit cleaner by separating out the action to an enum so actions are not hard coded to the text on the card.
pub struct Card {
text: String,
action: CardAction,
}
// Note: enums which can also hold data, are more commonly referred to as
// "tagged unions" in computer science. It can be a pain to search for them if
// you don't know what they are called.
pub enum CardAction {
ProceedToGo,
GoToJail,
GainOrLoseMoney(i64),
// etc.
}
On a similar note, it looks like you are trying to be memory conscious by using the smallest type required for a given value. I would recommend against this thinking. If a value of 253u8 is equally as invalid as 324234i32, then the smaller type is not doing anything to help you. You might as well use i32/u32 or i64/u64 since most systems will have an easier time operating on these types. The same thing goes for indices and using other integer types instead of usize since choosing to use another type will only give you more work converting it to and from a usize.
Sharing Owned References
Depending on your design philosophy you might want to store a reference to a struct in multiple places. This can be done using a reference counter Rc<T>. Here are some quick examples. Note that these can not be shared between threads.
let property: Rc<Property> = Rc::new(Property::new(/* etc */));
// Holds an owned reference to the same property as property that can be accessed immutably
let ref_to_property: Rc<Property> = property.clone();
// Or if you want interior mutability you can use Rc<RefCell<T>> instead.
let mutable_property = Rc::new(RefCell::new(Property::new(/* etc */)));
I would like to write a simulation algorithm in Rust that has three main parts. The first part is a struct that maintains the current state of the system and associated methods to make allowed state transitions. The second part is a simulation strategy, this tells which state transition(s) to make next (e.g. I will have a slow but accurate simulation strategy and a quick but approximate strategy). Finally, I have a way to serialise the system after a simulation step was taken (e.g. one method writes the system to csv while another one to json).
I would like to choose both the simulation strategy and the serialisation method at run time.
Coming from Python, my first try for the main simulation loop looked like this:
let mut system = System { ... };
let simulator = GillespieAlgorithm { system: &mut system, ... }; // this will be dynamic eventually
let output_formatter = CSVFormatter { system: &system, ... }; // this will be dynamic eventually
output_formatter.start();
for _ in 1..100 {
simulator.step();
output_formatter.write_current_state();
}
output_formatter.stop();
Of course, this doesn't work because I want to borrow system twice with one of them being a mutable reference.
What would be the idiomatic Rust solution to this problem? My understanding is that I should somehow attach a behaviour dynamically to System instead of passing system to other structs.
You've mentioned in the comments, that you want to keep the &mut system in your simulation. Thats fine and you can still use system, as long as you're getting it via the GillespieAlgorithm. If you're fine to pass it to the formatter by method argument rather than constructor, this might be a solution for you (playground)
struct System();
struct GillespieAlgorithm<'a> { system: &'a mut System }
struct CSVFormatter();
fn main() {
let mut system = System();
let mut simulator : Box<dyn Algorithm> = Box::new(GillespieAlgorithm { system: &mut system }); // this will be dynamic eventually
let output_formatter: Box<dyn Formatter> = Box::new(CSVFormatter());
output_formatter.start();
for _ in 1..100 {
simulator.step();
output_formatter.write_current_state(simulator.borrow_system());
}
output_formatter.stop();
}
trait Algorithm {
fn step(&mut self) {}
fn borrow_system(&self) -> &System;
}
impl<'a> Algorithm for GillespieAlgorithm<'a> {
fn step(&mut self) {}
fn borrow_system(&self) -> &System {
self.system
}
}
trait Formatter {
fn start(&self);
fn write_current_state(&self, system: &System);
fn stop(&self);
}
impl Formatter for CSVFormatter {
fn start(&self) {}
fn write_current_state(&self, _system: &System) {}
fn stop(&self) {}
}
If you don't need the entire System in the CSVFormatter, you could also return a dedicated state struct in step() and pass this to your formatter.
I have two structs Node and Communicator. Node contains "business-logic" while Communicator contains methods for sending and receiving UDP messages. Node needs to call methods on Communicator when it wants to send messages and Communicator needs to call methods on Node when it receives a UDP message. If they are the same struct, there is no problem at all. But I want to separate them because they clearly have different responsibilities. Having it all in one struct would become unmanageable. My code looks as follows:
fn main() {
use std::sync::{Arc, Mutex, Condvar, Weak};
use std::thread;
pub struct Node {
communicator: Option<Arc<Communicator>>
}
impl Node {
pub fn new() -> Node {
Node {
communicator: None
}
}
pub fn set_communicator(&mut self, communicator: Arc<Communicator>) {
self.communicator = Some(communicator);
}
}
pub struct Communicator {
node: Option<Weak<Node>>
}
impl Communicator {
pub fn new() -> Communicator {
Communicator {
node: None
}
}
pub fn set_node(&mut self, node: Weak<Node>) {
self.node = Some(node);
}
}
let mut my_node = Arc::new(Node::new());
let mut my_communicator = Arc::new(Communicator::new());
Arc::get_mut(&mut my_node).unwrap().set_communicator(Arc::clone(&my_communicator));
//Arc::get_mut(&mut my_communicator).unwrap().set_node(Arc::downgrade(&my_node));
}
My code crashes, as I predicted, if I uncomment both of the two last lines. But it is something like that I want to achieve.
I see a couple of options:
Use Mutex or RwLock to gain interior mutability. But that gives a performance penalty.
Use Cell or RefCell. But they are not thread-safe.
Use AtomicCell from crossbeam. Looks like the best option so far. But what about performance?
Use unsafe. But where? And how to ensure the code remains memory safe in spite of unsafe?
In theory I could split Communicator into a Sender and a Receiver, so that there would be no cyclical references. But for my program, I know I will have a similar situation in the future where this will not be possible.
It seems that since I just split a struct into two, for code-structuring reasons, and gaining no new functionality, there should be a way to do this without having to pay any performance penalty like Mutex would give.