Rust Setting static lifetime after initialization - multithreading

I have a graph struct which looks like this:
pub struct Graph {
pub nodes: Vec<Node>,
pub edges: Vec<Edge>
}
My program reads the nodes and edges from an external file and after that they will not be changed anymore.
How can i set the Graph to have a static lifetime so i can use it in multiple threads?

Related

Serialize complicated data structure

I'm having trouble serializing the following struct. I have narrowed it down to, that the problem lies within the variable objects, containing trait structs within a HashMap. I'll try to explain my circumstances:
I have the following main struct, which i'm interested in obtaining data from:
#[derive(Serialize)]
pub struct System {
pub objects: HashMap<(u32, u32), Box<dyn CommonObjects>>,
pub paths: Vec<Path>,
pub loops: Vec<Loop>,
pub physics: Physics,
pub run_limit_loops: u32,
pub run_limit_paths: u32,
pub error_tol_loops: f64,
pub error_tol_paths: f64,
pub unknown_flow_outlets: u32,
pub unknown_pumps_id: u32,
pub system: u32,
}
The kind of structs which are contained within objects are as the following:
pub struct Outlet {
// Variables found in all structs contained within 'objects'
pub Q: f64,
pub hL: f64,
pub ob: u32,
pub id: u32,
pub active: bool,
pub system: u32,
pub con_Q: HashMap<u32, f64>,
// Variables found only in this struct.
pub p_dyn: f64,
pub flow_specified: String,
pub submerged: bool,
}
pub struct Pipe {
// Variables found in all structs contained within 'objects'
pub Q: f64,
pub hL: f64,
pub ob: u32,
pub id: u32,
pub active: bool,
pub system: u32,
pub con_Q: HashMap<u32, f64>,
// Variables found only in this struct.
pub Re: f64,
pub f: f64,
}
These struct has some variables which are only used within themselves, and some variables which are common for all structs contained within objects (p_dyn is only used within Outlet, while Q is used for all structs contained within objects) To get these variables, get-function are defined, both within the local struct, and by the trait CommonObject. All i am interested in, is serializing objects in order to get all the variables in a string format, both the common ones, and the ones only appearing locally within a struct, so that i can send the variables to other programs to further visualization.
In this following code the error occurs:
// Where the system struct originates from.
let systems: Vec<System> = system_analyse::analyse_system(data);
// I try to only serialize the objects contained within one of the system structs.
let jsonstringresult = serde_json::to_string(&systems[0].objects);
match jsonstringresult {
Ok(v) => {
println!("{:?}", &v);
v
},
Err(e) => {
// The error message originates from here.
println!("An error occured at serializing: {}", e);
String::new()
}
}
I get the following error:
An error occured at serializing: key must be a string
I have found this thread discussing the issue of serializing dynamic traits, and i've followed the instructions and added the following to the trait:
pub trait CommonObjects: erased_serde::Serialize {
...
}
serialize_trait_object!(CommonObjects);
Which makes the code compile in the first place. I've also found this site getting the same error, but the issue there seems to be with enums. Maybe the problem in this site is related to my problem, but i cant figure out how if so.
I'm open to all sort of feedback and even fundamentally change the structure of the code if so necessary.
A quick way to serialize a HashMap with non-string keys to Json is to use the serde_as macro from the serde_with crate.
use serde_with::serde_as;
#[serde_as]
#[derive(Serialize)]
pub struct System {
#[serde_as(as = "Vec<(_, _)>")]
pub objects: HashMap<(u32, u32), Box<dyn CommonObjects>>,
//...
}
The #[serde_as(as = "Vec<(_, _)>")] encodes the map as a sequence of tuples, representing pairs of keys and values. In Json, this will become an array of 2-element arrays.

Directly parse data on nested proc macro

Suppose this:
#[route(base_path="/api")]
pub struct Type;
impl Type {
#[route(path="/v1/handler")]
pub fn handler() {}
#[route(path="/v1/handler2")]
pub fn handler2() {}
}
I want to parse in any kind of data struct some mapping between the base_path and the path attached to the methods on the impl block. Imagine any HashMap<K, V> where base_path are the keys and path it's related values.
// pseudo struct internal data representation
[
{
struct: MyRestController,
base_path: "/api",
paths: [
"handler", "handler2"
]
},
// other types registered
]
Is it possible to know with the proc-macro attached to the struct, detect the attributes attached to the methods on the impl block and made the parse in one type? (When the macro attached to the struct runs).
You probably want to write your macro so that it expects the following:
pub struct Type;
#[route(base_path="/api")]
impl Type {
#[route(path="/v1/handler")]
pub fn handler() {}
#[route(path="/v1/handler2")]
pub fn handler2() {}
}
That is, it wouldn't know anything about the struct itself (i.e. its fields), but it will have full access to the impl block, including attributes on individual functions.

How to have multiple references for a single node in a tree structure using Rust

Trying to create a tree in rust with the following struct:
pub struct Node{
pub children: Vec<Box<Node>>,
pub parent: Option<Box<Node>>,
pub value: f32,
//.....
}
To build a new node the following function is used:
pub fn build_node(parent: Option<Box<Node>>)-> Node{
Node{
children: vec![],
parent,
value: 0.0,
}
}
When trying to add nodes, for example with:
let mut root_nd = tree::build_node(None, 5, state);
let mut next_nd = tree::build_node(Some(Box::new(root_nd)), 2);
root_nd.children.push(Box::new(next_nd));
There will be errors, because I am borrowing for example root_nd and then trying to add next_nd to the root.children list, and even if there wasnt this error I would still need to have a reference for next_nd after adding it to the children of root_nd. I know that in rust it is not possible to have several mutable references simultaneously for the same element. So the question is how is it possible to make a tree-like data structure, with bi-directional references in rust? In my head this is a conflict since rust does not want multiple references but I need a node in the middle of the tree to be referenced by both his parent node and his children nodes.
I've been meddling with trees in Rust for quite a bit recently. To work with trees in rust, you will need Rc (A single-threaded reference-counting pointer) so that you can have multiple ownership. And you'll also need RefCell to enable interior mutability since multiple mutable references are not allowed by the compiler. With Rc and RefCell, you can define your TreeNode as following:
use std::rc::Rc;
use std::cell::RefCell;
pub struct TreeNode {
pub children: Vec<Rc<RefCell<TreeNode>>>,
pub parent: Option<Rc<RefCell<TreeNode>>>,
pub value: f32,
}
And here is one way to create two nodes that references each other:
impl TreeNode {
#[inline]
pub fn new(value: f32) -> Self {
TreeNode {
value,
children: vec![],
parent: None
}
}
}
let mut root_nd = Rc::new(RefCell::new(TreeNode::new(5.0)));
let mut child_nd = Rc::new(RefCell::new(TreeNode::new(2.0)));
child_nd.borrow_mut().parent = Some(root_nd.clone()); // use Rc::clone to create a new reference to root_nd
root_nd.borrow_mut().children.push(child_nd.clone());
Since we use Rc::clone to create a new reference to the node, root_nd and child_nd are not consumed and can still be accessed in later program.
More examples on Trees in Rust:
leetcode 95 Unique Binary Search
Trees
leetcode 94 Binary Tree Inorder
Traversal
leetcode 100 Is Same
Tree

How to return a struct from inside an Rc?

I have a struct wrapped in a Rc called Data. I want to create a function that returns the Data inside the Rc:
use std::rc::Rc;
fn main() {
pub struct Data {
pub tag: Vec<u8>,
pub bytes: [u8; 32],
pub length: u8,
}
pub struct SharedData {
pointer: Rc<Data>,
}
pub fn get_data(shared: SharedData) -> Data {
shared.pointer
}
}
In this contrived example I have stripped away a lot of my surrounding code so the idea might look strange.
From the docs, Rc is a reference-counted pointer. It is used for sharing data between different sections of code without needing to have a single owner. However, this also hints at the reason your code is unable to directly return a Data: in order to return that, it must first find a way to satisfy the borrow checker's notion of data ownership.
The options are generally to take sole ownership of the data, or clone it. In the edited code included below, I've changed get_data to first get access to Data by dereferencing the pointer (Rc implements the Deref trait), and then cloning the struct before returning it. Because the return type is Data (and not &Data) this means that this value is now owned by the caller. Because it was cloned, any mutations will not be visible to any code still referring to the Rc<Data>. Note that I needed to derive the Clone trait on Data before the clone method was available.
#![allow(unused)]
use std::rc::Rc;
use std::error;
use std::error::Error;
fn main() {
#[derive(Clone)]
pub struct Data {
pub tag: Vec<u8>,
pub bytes: [u8; 32],
pub length: u8,
}
pub struct SharedData {
pointer: Rc<Data>,
}
pub fn get_data(shared: SharedData) -> Data {
// Dereference the pointer, and then return
// a copy of its data
(*shared.pointer).clone()
}
pub fn get_data_length(shared: SharedData) -> u8 {
// For simpler types, you can just access the fields inside
// Data directly
shared.pointer.length
}
}
I've also included a get_data_length to demonstrate that individual members of the shared data can also be returned without formally cloning.
Finally, if you intend to have shared mutable access to data, a Cell might be a more appropriate choice than an Rc.
If you want to remove the Rc, try_unwrap might be worth taking a look at. You can unwrap the result if you are sure that no other reference will exist.

Impl Send for Bindgen-generated pointer type

I am trying to send an FFI pointer type to another thread. The struct it points to has been generated by bindgen from opensles-sys
Here is my wrapper struct:
pub struct AndroidAudioIO {
sl_output_buffer_queue: NonNull<SLObjectItf>,
}
unsafe impl Send for AndroidAudioIO{}
The SLObjectItf type is an alias for *const *const SLObjectItf_ whose definition is generated by bindgen. It's a collection of FFI function pointers.
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct SLObjectItf_ {
pub Realize: ::std::option::Option<
unsafe extern "C" fn(self_: SLObjectItf, async: SLboolean) -> SLresult,
>,
// More of the same pattern, only extern "C" function pointers, no data
}
I tried adding unsafe impl Send for SLObjectItf_{} and other variants to no avail.
error[E0277]: `std::ptr::NonNull<*const *const opensles::bindings::SLObjectItf_>` cannot be shared between threads safely
--> src/lib.rs:12:1
|
12 | / lazy_static! {
13 | | static ref engine:Option<mynoise::Engine<Box<audio::AndroidAudioIO>>> = None;
14 | | }
| |_^ `std::ptr::NonNull<*const *const opensles::bindings::SLObjectItf_>` cannot be shared between threads safely
|
= help: within `audio::AndroidAudioIO`, the trait `std::marker::Sync` is not implemented for `std::ptr::NonNull<*const *const opensles::bindings::SLObjectItf_>`
= note: required because it appears within the type `audio::AndroidAudioIO`
The reason why I only care about Send but not Sync is that a single thread (the RT audio thread) interacts with this struct, but it is being created on another thread, hence the need to Send the pointer across to the correct thread.
The following code reproduces the same problem (assuming Engine only retains AndroidAudioIO at type-level, so that it can produce such a handler at a later date; it works by direct composition as well).
#[macro_use]
extern crate lazy_static;
use std::marker::PhantomData;
use std::ptr::NonNull;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct SLObjectItf;
pub struct AndroidAudioIO {
sl_output_buffer_queue: NonNull<SLObjectItf>,
}
unsafe impl Send for AndroidAudioIO {}
#[derive(Debug)]
pub struct Engine<T>(PhantomData<T>);
lazy_static! {
static ref engine: Option<Engine<AndroidAudioIO>> = None;
}
(Playground)
The issue here is that this Engine entity is in a global static variable, which immediately makes it shared across all threads. This requires Sync, but Engine was not given an implementation of Sync because AudioAndroidIO does not implement Sync. Indeed, regardless of whether the engine contains the audio I/O handler as an attribute or that information only exists at type level, even PhantomData inherits these trait implementations directly from its parameter type. Quoting from the docs:
impl<T: ?Sized> Send for PhantomData<T>
where
T: Send,
impl<T: ?Sized> Sync for PhantomData<T>
where
T: Sync
This is likely a case where Engine is OK to have Sync (although PhantomData chooses this safe behaviour of avoiding assumptions about the inner type). To solve this, first make absolute sure that Engine is thread-safe. Then, manually implement Sync for this one.
unsafe impl<T> Sync for Engine<T> {}
I tried adding unsafe impl Send for SLObjectItf_{} and other variants to no avail.
Well, that would have generally been a Bad Idea™ anyway. Implementing Send and/or Sync should be done on top of a safe, high level abstraction of your bindings.

Resources