I am trying to create a network of nodes in Rust, where I want every node in the network to be aware of every other connected node. I thought that this could be done with weak Rc's, like this:
use std::cell::Cell;
use std::cell::RefCell;
use std::rc::Rc;
use std::rc::Weak;
struct Node {
name: String,
known_nodes: Rc<RefCell<Vec<Weak<Node>>>>,
}
impl Node {
fn connect_to_network(&mut self) {
self.known_nodes
.borrow_mut()
.push(Rc::downgrade(&Rc::new(*self)));
}
}
fn main() {
let known_nodes = Rc::new(RefCell::new(Vec::new()));
let node_one = Node {
name: "node1",
known_nodes: known_nodes.copy(),
};
node_one.connect_to_network();
let node_two = Node {
name: "node2",
known_nodes: known_nodes.copy(),
};
node_two.connect_to_network();
}
This however yields
cannot move out of borrowed content
at:
self.known_senders.borrow_mut().push(Rc::downgrade(&Rc::new(*self)));
Because *self is moved out of borrowed content in the &Rc::new(*self).
Any ideas, on how each node can keep track of all the other nodes in the network?
You should separate your node and your network, because your network must take the ownership of your node to create an Rc (or at least, it must take an already created Rc). Here is a better design that achieves what you want:
use std::rc::Rc;
use std::rc::Weak;
use std::cell::RefCell;
#[derive(Debug)]
struct Node {
name: String,
}
#[derive(Default, Debug)]
struct Network {
nodes: Rc<RefCell<Vec<Weak<Node>>>>,
}
impl Network {
fn add_node(&mut self, node: Node) -> Rc<Node> {
let node = Rc::new(node);
self.nodes.borrow_mut().push(Rc::downgrade(&node));
node
}
}
fn main() {
let mut network = Network::default();
let node_1 = Node { name: "node_1".into() };
let node_2 = Node { name: "node_2".into() };
let _node_1 = network.add_node(node_1);
let _node_2 = network.add_node(node_2);
}
If you want to store a reference to self, you can do this:
use std::cell::RefCell;
use std::rc::Rc;
use std::rc::Weak;
type MutableNode = Rc<RefCell<Node>>;
type Network = Rc<RefCell<Vec<Weak<RefCell<Node>>>>>;
struct Node {
name: String,
others: Network,
}
impl Node {
fn new(name: String) -> MutableNode {
let node = Rc::new(RefCell::new(Node {
name,
others: Rc::new(RefCell::new(Vec::new())),
}));
{
let tmp = node.borrow();
tmp.others.borrow_mut().push(Rc::downgrade(&node));
}
node
}
fn add_node(&mut self, name: String) -> MutableNode {
let others = self.others.clone();
let node = Rc::new(RefCell::new(Node { name, others }));
self.others
.borrow_mut()
.push(Rc::downgrade(&node));
node
}
fn len(&self) -> usize {
self.others.borrow().len()
}
}
fn main() {
let node_0 = Node::new("node_0".into());
let node_1 = node_0.borrow_mut().add_node("node_1".into());
let node_2 = node_0.borrow_mut().add_node("node_2".into());
assert_eq!(node_0.borrow().len(), 3);
assert_eq!(node_1.borrow().len(), 3);
assert_eq!(node_2.borrow().len(), 3);
}
Rc::new(value:T) consume the value.Your function only borrow it, so you can't call Rc::new(*self)
I would recommend you to create a Network struct like the above answer. Or you can wrap your node in Rc<RefCell<Node>> like this:
use std::cell::RefCell;
use std::rc::Rc;
use std::rc::Weak;
#[derive(Debug)]
struct Node {
name: String,
known_nodes: Rc<RefCell<Vec<Weak<RefCell<Node>>>>>,
}
impl Node {
fn connect_to_network(&mut self,ref_to_self: Weak<RefCell<Node>>) {
self.known_nodes
.borrow_mut()
.push(ref_to_self);
}
}
fn main() {
let known_nodes = Rc::new(RefCell::new(Vec::new()));
let node_one = Rc::new(RefCell::new(Node {
name: "node1".into(),
known_nodes: known_nodes.clone(),
}));
node_one.borrow_mut().connect_to_network(Rc::downgrade(&node_one));
let node_two = Rc::new(RefCell::new(Node {
name: "node2".into(),
known_nodes: known_nodes.clone(),
}));
node_two.borrow_mut().connect_to_network(Rc::downgrade(&node_two));
println!("{:?}",known_nodes.borrow()[0].upgrade());
println!("{:?}",known_nodes.borrow()[1].upgrade());
drop(node_one);
drop(node_two);
println!("{:?}",known_nodes.borrow()[0].upgrade());
println!("{:?}",known_nodes.borrow()[1].upgrade());
}
Which in this case you don't really need connect_to_network function, you can just add each Weak<RefCell<Node>> to known_nodes directly
If you want the code to look cleaner, you can introduce a new type alias to Rc<RefCell<Node>> like this
struct Node {
name: String,
known_nodes: Rc<RefCell<Vec<Weak<RefCell<Node>>>>>,
}
type RcNode = Rc<RefCell<Node>>;
trait Connectable {
fn connect_to_network(&self);
}
impl Connectable for RcNode {
fn connect_to_network(&self){
let node = self.borrow_mut();
node.known_nodes.borrow_mut().push(Rc::downgrade(self));
}
}
so then you can call
let node_one:RcNode = Rc::new(RefCell::new(Node {
name: "node1".into(),
known_nodes: known_nodes.clone(),
}));
node_one.connect_to_network();
Related
Please suggest how to implement such a design. I understand that mediator needs to be passed as a reference. But I can't figure out the lifetime parameter.
//Colleague
struct Switcher {
state: bool,
mediator: SyncMediator
}
impl Switcher {
fn sync(self) {
self.mediator.sync(self.state);
}
fn get_state(&self) -> bool {
return self.state;
}
fn set_state(&mut self, value: bool) {
self.state = value;
}
}
//ConcreteMediator
struct SyncMediator {
switchers: Vec<Switcher>
}
impl SyncMediator {
fn sync(mut self, state: bool) {
for i in 0..self.switchers.len() {
self.switchers[i].set_state(state);
}
}
fn add(&mut self, switcher: Switcher) {
self.switchers.push(switcher);
}
}
fn main() {
//Client
let mediator = SyncMediator {
switchers: vec![] };
let mut switcher1 = Switcher {
mediator: mediator, state: false };
let switcher2 = Switcher {
mediator: mediator, state: false };
let switcher3 = Switcher {
mediator: mediator, state: false };
switcher1.set_state(true);
let mut state2 = switcher2.get_state();
//state2 is false
let mut state3 = switcher3.get_state();
//state3 is false
println!("state2 is {state2}");
println!("state2 is {state3}");
switcher1.sync();
state2 = switcher2.get_state();
//state2 is true
state3 = switcher3.get_state();
//state3 is true
println!("state2 is {state2}");
println!("state2 is {state3}");
}
You can find a detailed explanation of the Mediator pattern in Rust here: https://github.com/fadeevab/mediator-pattern-rust
In short, there are two approaches:
Cross-Referencing with Rc<RefCell<..>>. It's kind of mimicking a classic OOP with Rust.
In your case, you'd have
let mediator = Rc::new(RefCell::new(SyncMediator { .. }));
let switcher1 = Rc::new(Switcher { mediator: mediator.clone(), .. });
{
let mut mediator = mediator.borrow_mut();
mediator.add(switcher1.clone());
mediator.add(switcher2.clone());
}
passing the references across the objects. I call it a "naive" approach.
Top-down ownership approach, which adapts a classic Mediator definition to what could be safely and idiomatically done in the Rust language.
You should create a mediator, switchers, then you should move ownership of switchers to the mediator, and overall do like this:
let mut mediator = SyncMediator { .. };
let switcher1 = Switcher { name: "switcher1", .. };
mediator.add(switcher1);
mediator.add(switcher2);
// Then, work with the mediator at the top level.
mediator.set_state("switcher1", true);
where the switcher receives the mediator object as a function parameter:
impl Switcher {
fn set_state(&mut self, value: bool, mediator: &mut SyncMediator) {
self.state = value;
mediator.sync();
}
}
P.S.: The following image illustrates an example of interactions between visual components, where the Dialog is the Mediator.
I am experimenting with tide and glommio using Rust. Consider following code:
use std::io::Result;
use glommio::prelude::*;
use tide::Request;
use tide::Response;
use tide::http::mime;
use tide::prelude::*;
#[async_std::main]
async fn main() -> Result<()> {
let mut app = tide::new();
app.at("/orders/shoes").post(order_shoes);
let builder = LocalExecutorPoolBuilder::new(16);
let handles = builder.on_all_shards(|| async move {
app.listen("127.0.0.1:8080").await;
}).unwrap();
handles.join_all();
Ok(())
}
#[derive(Debug, Deserialize)]
struct Animal {
name: String,
legs: u8,
}
pub async fn order_shoes(mut req: Request<()>) -> tide::Result {
let Animal { name, legs } = req.body_json().await?;
let res = Response::builder(203)
.body("Hi")
.header("custom-header", "value")
.content_type(mime::HTML)
.build();
Ok(res)
}
Now in case the request has legs greater than 255 then it should respond with error but I am unable to figure out how to return that to caller.
I've failed to get this code past the borrow-checker:
use std::sync::Arc;
use std::thread::{sleep, spawn};
use std::time::Duration;
#[derive(Debug, Clone)]
struct State {
count: u64,
not_copyable: Vec<u8>,
}
fn bar(thread_num: u8, arc_state: Arc<State>) {
let state = arc_state.clone();
loop {
sleep(Duration::from_millis(1000));
println!("thread_num: {}, state.count: {}", thread_num, state.count);
}
}
fn main() -> std::io::Result<()> {
let mut state = State {
count: 0,
not_copyable: vec![],
};
let arc_state = Arc::new(state);
for i in 0..2 {
spawn(move || {
bar(i, arc_state.clone());
});
}
loop {
sleep(Duration::from_millis(300));
state.count += 1;
}
}
I'm probably trying the wrong thing.
I want one (main) thread which can update state and many threads which can read state.
How should I do this in Rust?
I have read the Rust book on shared state, but that uses mutexes which seem overly complex for a single writer / multiple reader situation.
In C I would achieve this with a generous sprinkling of _Atomic.
Atomics are indeed a proper way, there are plenty of those in std (link. Your example needs 2 fixes.
Arc must be cloned before moving into the closure, so your loop becomes:
for i in 0..2 {
let arc_state = arc_state.clone();
spawn(move || { bar(i, arc_state); });
}
Using AtomicU64 is fairly straight forward, though you need explicitly use newtype methods with specified Ordering (Playground):
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use std::thread::{sleep, spawn};
use std::time::Duration;
#[derive(Debug)]
struct State {
count: AtomicU64,
not_copyable: Vec<u8>,
}
fn bar(thread_num: u8, arc_state: Arc<State>) {
let state = arc_state.clone();
loop {
sleep(Duration::from_millis(1000));
println!(
"thread_num: {}, state.count: {}",
thread_num,
state.count.load(Ordering::Relaxed)
);
}
}
fn main() -> std::io::Result<()> {
let state = State {
count: AtomicU64::new(0),
not_copyable: vec![],
};
let arc_state = Arc::new(state);
for i in 0..2 {
let arc_state = arc_state.clone();
spawn(move || {
bar(i, arc_state);
});
}
loop {
sleep(Duration::from_millis(300));
// you can't use `state` here, because it moved
arc_state.count.fetch_add(1, Ordering::Relaxed);
}
}
I'm trying to write a Rocket / Juniper / Rust based GraphQL Server using PickleDB - an in-memory key/value store.
The pickle db is created / loaded at the start and given to rocket to manage:
fn rocket() -> Rocket {
let pickle_path = var_os(String::from("PICKLE_PATH")).unwrap_or(OsString::from("pickle.db"));
let pickle_db_dump_policy = PickleDbDumpPolicy::PeriodicDump(Duration::from_secs(120));
let pickle_serialization_method = SerializationMethod::Bin;
let pickle_db: PickleDb = match Path::new(&pickle_path).exists() {
false => PickleDb::new(pickle_path, pickle_db_dump_policy, pickle_serialization_method),
true => PickleDb::load(pickle_path, pickle_db_dump_policy, pickle_serialization_method).unwrap(),
};
rocket::ignite()
.manage(Schema::new(Query, Mutation))
.manage(pickle_db)
.mount(
"/",
routes![graphiql, get_graphql_handler, post_graphql_handler],
)
}
And I want to retrieve the PickleDb instance from the Rocket State in my Guard:
pub struct Context {
pickle_db: PickleDb,
}
impl juniper::Context for Context {}
impl<'a, 'r> FromRequest<'a, 'r> for Context {
type Error = ();
fn from_request(_request: &'a Request<'r>) -> request::Outcome<Context, ()> {
let pickle_db = _request.guard::<State<PickleDb>>()?.inner();
Outcome::Success(Context { pickle_db })
}
}
This does not work because the State only gives me a reference:
26 | Outcome::Success(Context { pickle_db })
| ^^^^^^^^^ expected struct `pickledb::pickledb::PickleDb`, found `&pickledb::pickledb::PickleDb`
When I change my Context struct to contain a reference I get lifetime issues which I'm not yet familiar with:
15 | pickle_db: &PickleDb,
| ^ expected named lifetime parameter
I tried using 'static which does make rust quite unhappy and I tried to use the request lifetime (?) 'r of the FromRequest, but that does not really work either...
How do I get this to work? As I'm quite new in rust, is this the right way to do things?
I finally have a solution, although the need for unsafe indicates it is sub-optimal :)
#![allow(unsafe_code)]
use pickledb::{PickleDb, PickleDbDumpPolicy, SerializationMethod};
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::env;
use std::path::Path;
use std::time::Duration;
pub static mut PICKLE_DB: Option<PickleDb> = None;
pub fn cache_init() {
let pickle_path = env::var(String::from("PICKLE_PATH")).unwrap_or(String::from("pickle.db"));
let pickle_db_dump_policy = PickleDbDumpPolicy::PeriodicDump(Duration::from_secs(120));
let pickle_serialization_method = SerializationMethod::Json;
let pickle_db = match Path::new(&pickle_path).exists() {
false => PickleDb::new(
pickle_path,
pickle_db_dump_policy,
pickle_serialization_method,
),
true => PickleDb::load(
pickle_path,
pickle_db_dump_policy,
pickle_serialization_method,
)
.unwrap(),
};
unsafe {
PICKLE_DB = Some(pickle_db);
}
}
pub fn cache_get<V>(key: &str) -> Option<V>
where
V: DeserializeOwned + std::fmt::Debug,
{
unsafe {
let pickle_db = PICKLE_DB
.as_ref()
.expect("cache uninitialized - call cache_init()");
pickle_db.get::<V>(key)
}
}
pub fn cache_set<V>(key: &str, value: &V) -> Result<(), pickledb::error::Error>
where
V: Serialize,
{
unsafe {
let pickle_db = PICKLE_DB
.as_mut()
.expect("cache uninitialized - call cache_init()");
pickle_db.set::<V>(key, value)?;
Ok(())
}
}
This can be simply imported and used as expected, but I think I'll run into issues when the load gets to high...
In the past this code compiled, but recently it no longer is accepted (I believe since RFC 738). I want to type-parameterize the VertexBuffer, but it doesn't actually hold any vertices, rather the GPU holds the vertices, and the struct only holds an OpenGL buffer_id:
pub struct VertexBuffer<V: Vertex> {
buffer_id: GLuint,
num_vertices: usize,
}
The new function fills the buffer:
impl<V: Vertex> VertexBuffer<V> {
pub fn new(data: &Vec<V>) -> VertexBuffer<V>
{
let buffer_id = unsafe {
let mut id: GLuint = 0;
gl::GenBuffers(1, &mut id);
gl::BindBuffer(gl::ARRAY_BUFFER, id);
gl::BufferData(gl::ARRAY_BUFFER,
(mem::size_of::<V>() * data.len()) as GLsizeiptr,
mem::transmute(&data[0]),
gl::STATIC_DRAW);
id
};
VertexBuffer {
buffer_id: buffer_id,
num_vertices: data.len(),
}
}
....
}
I now get these errors:
src/vertex_buffer.rs:10:25: 10:26 error: parameter `V` is never used
src/vertex_buffer.rs:10 pub struct VertexBuffer<V: Vertex> {
src/vertex_buffer.rs:10:25: 10:26 help: consider removing `V` or using a marker such as `core::marker::PhantomData`
src/vertex_buffer.rs:10 pub struct VertexBuffer<V: Vertex> {
FYI, other functions in the impl such as pre_render() and post_render() use the V type to do their work, calling things like
let attribute_data = Vertex::attribute_data(None::<V>);
You haven't shown enough code to rule this out, so I'd suggest moving your type to the function(s):
impl VertexBuffer {
pub fn new<V: Vertex>(data: &Vec<V>) -> VertexBuffer<V> {
let buffer_id = unsafe {
let mut id: GLuint = 0;
gl::GenBuffers(1, &mut id);
gl::BindBuffer(gl::ARRAY_BUFFER, id);
gl::BufferData(gl::ARRAY_BUFFER,
(mem::size_of::<V>() * data.len()) as GLsizeiptr,
mem::transmute(&data[0]),
gl::STATIC_DRAW);
id
};
VertexBuffer {
buffer_id: buffer_id,
num_vertices: data.len(),
}
}
....
}
If you do need to use PhantomData, try something like:
struct VertexBuffer<V> {
buffer_id: u32, // or whatever
num_vertices: u32, // or whatever
marker: std::marker::PhantomData<V>,
}
impl<V: Vertex> VertexBuffer<V> {
pub fn new(data: &Vec<V>) -> VertexBuffer<V> {
let buffer_id = unsafe {
let mut id: GLuint = 0;
gl::GenBuffers(1, &mut id);
gl::BindBuffer(gl::ARRAY_BUFFER, id);
gl::BufferData(gl::ARRAY_BUFFER,
(mem::size_of::<V>() * data.len()) as GLsizeiptr,
mem::transmute(&data[0]),
gl::STATIC_DRAW);
id
};
VertexBuffer {
buffer_id: buffer_id,
num_vertices: data.len(),
marker: std::marker::PhantomData,
}
}
....
}