Unique pointers to state + callbacks in Rust - rust

I am trying to bundle state in a struct together with callbacks that mutate the state. It works fine when I use managed pointers:
struct StateAndCallbacks01 {
state: #mut int,
inc: #fn(),
dec: #fn()
}
let state01: #mut int = #mut 0;
let inc01: #fn() = || {
*state01 += 1;
};
let dec01: #fn() = || {
*state01 -= 1;
};
let state_cbs_01 = #StateAndCallbacks01 {
state: state01,
inc: inc01,
dec: dec01
};
(state_cbs_01.inc)();
println(fmt!("state: %d", *state_cbs_01.state));
(state_cbs_01.dec)();
println(fmt!("state: %d", *state_cbs_01.state));
Next, I want to send this structure to another task, and thus have to switch to unique pointers everywhere. I cannot make that work: "error: obsolete syntax: const or mutable owned pointer"
struct StateAndCallbacks02 {
state: ~mut int,
inc: ~fn(),
dec: ~fn()
}
let state02: ~mut int = ~mut 0;
let inc02: ~fn() = || {
*state02 += 1;
};
let dec02: ~fn() = || {
*state02 -= 1;
};
let state_cbs_02 = ~StateAndCallbacks02 {
state: state02,
inc: inc02,
dec: dec02
};
let (port, chan): (Port<bool>, Chan<bool>) = stream();
do spawn {
(state_cbs_02.inc)();
println(fmt!("state: %d", *state_cbs_02.state));
(state_cbs_02.dec)();
println(fmt!("state: %d", *state_cbs_02.state));
chan.send(true);
};
let result = port.recv();
println(fmt!("result: %s", result));
Any suggestions? Any better ways to send callbacks across tasks?

Instead of keeping the functions as fields in the struct you can instead add methods to the struct.
struct Foo {
data: int
}
impl Foo {
fn inc(&mut self) {
self.data += 1;
}
}
The impl syntax lets you define methods on the struct. Which you can later call:
let mut my_foo = Foo { data: 0 };
my_foo.inc();
You have to declare my_foo as mutable since the inc method needs to take a mutable reference to it.
The reason for the obsolete syntax error is because doing ~mut 0 is deprecated since mutability is determined by who 'owns' the object. What you'd have to do instead is let mut foo = ~0. The variable foo is the 'owner' and thus is where you declare the mutability. #-pointers are special in that they don't inherit mutability and are managed by a task local GC. (Sections 8 & 9 of the Rust tutorial better explain this)
So with all that put together, you could write your original code like so:
struct State {
data: int
}
impl State {
fn inc(&mut self) {
self.data += 1;
}
fn dec(&mut self) {
self.data -= 1;
}
}
fn main() {
let state = State {
data: 0
};
let (port, chan) = stream();
do spawn {
let mut state = state;
state.inc();
println(fmt!("State: %d", state.data));
state.dec();
println(fmt!("State: %d", state.data));
chan.send(true);
};
let result = port.recv();
println(fmt!("Result: %?", result));
}

Related

How to properly initialize a struct in Rust, with good enough encapsulations?

How to properly initialize a struct in Rust, with good enough encapsulations?
Or more naively:
how to leverage object/instance methods in the initialization/constructing process of structs?
For example, as the initialization block in Kotlin:
private class BinaryIndexedTree(nums: IntArray) {
private val nNums = nums.size
private val fenwick = IntArray(nNums + 1) { 0 }
// where to put this block in Rust?
init {
for (idx in nums.indices) {
update(idx, nums[idx])
}
}
fun update(index: Int, value: Int) {
var idx = index + 1
while (idx <= nNums) {
fenwick[idx] += value
idx += (idx and -idx)
}
}
fun query(index: Int): Int {
var sum = 0
var idx = index + 1
while (idx > 0) {
sum += fenwick[idx]
idx -= (idx and -idx)
}
return sum
}
}
According to Rust Design Patterns, there is no regular constructors as other languages, the convention is to use an associated function.
Correspondingly, in Rust:
struct BinaryIndexedTree{
len_ns: isize,
fenwick: Vec<i32>,
}
impl BinaryIndexedTree{
pub fn new(nums: &Vec<i32>) -> Self{
let len_ns: usize = nums.len();
let fenwick: Vec<i32> = vec![0; len_ns + 1];
for (idx, num) in nums.iter().enumerate(){
// how to leverage `update()` for initialization
// update(idx as isize, num);
// or even earlier: where/how to put the initialization logic?
}
Self{
len_ns: len_ns as isize,
fenwick,
}
}
pub fn update(&mut self, index: isize, value: i32){
let mut idx = index + 1;
while idx <= self.len_ns{
self.fenwick[idx as usize] += value;
idx += (idx & -idx);
}
}
pub fn query(&self, index: isize) -> i32{
let mut sum: i32 = 0;
let mut idx = index + 1;
while idx > 0{
sum += self.fenwick[idx as usize];
idx -= (idx & -idx);
}
sum
}
}
Is there any way to properly leverage the update method?
As a rule of thumbs, how to properly handle the initialization work after the creation of (all the fields of) the struct?
The builder pattern is a way to go, which introduces much more code just for initialization.
Yes, you can construct the struct then call a function on it before returning it. There is nothing special about the new function name or how the struct is constructed at the end of the function.
pub fn new(nums: &Vec<i32>) -> Self {
let len_ns: usize = nums.len();
let fenwick: Vec<i32> = vec![0; len_ns + 1];
// Construct an incomplete version of the struct.
let mut new_self = Self {
len_ns: len_ns as isize,
fenwick,
};
// Do stuff with the struct
for (idx, num) in nums.iter().enumerate(){
new_self.update(idx as isize, num);
}
// Return it
new_self
}

Pass mutable pointer to function without initializing it first

use std::ptr::{addr_of_mut, null_mut};
use libc::{CLOCK_MONOTONIC, timer_create, timer_delete, timer_t};
fn main() {
let mut timer1: timer_t = null_mut();
unsafe {
let r = timer_create(CLOCK_MONOTONIC, null_mut(), addr_of_mut!(timer1));
if r == 0 {
timer_delete(timer1);
}
}
}
When calling timer_create(), the resulting timer ID is stored at variable timer1. I pass it as a mutable pointer, so that's the output variable.
How can I avoid initializing timer1 to null_mut() as in the code above knowing that it's guaranteed by the API to be safe ?
You can use MaybeUninit:
use std::mem::MaybeUninit;
use std::ptr::null_mut;
use libc::{CLOCK_MONOTONIC, timer_create, timer_delete, timer_t};
fn main() {
let mut timer1 = MaybeUninit::<timer_t>::uninit();
let timer1 = unsafe {
let r = timer_create(CLOCK_MONOTONIC, null_mut(), timer1.as_mut_ptr());
if r != 0 {
panic!("…");
}
timer1.assume_init()
};
unsafe {
timer_delete(timer1);
}
}

Dealing with so-called global variables in Rust

We all know that using global variables can lead to subtle bugs. I need to migrate Python programs to Rust, keeping the algorithm intact as far as possible. Once I have demonstrated Python-Rust equivalence there will be opportunities to debug and change the logic to fit Rust better. Here is a simple Python program using global variables, followed by my unsuccessful Rust version.
# global variable
a = 15
# function to perform addition
def add():
global a
a += 100
# function to perform subtraction
def subtract():
global a
a -= 100
# Using a global through functions
print("Initial value of a = ", a)
add()
print("a after addition = ", a)
subtract()
print("a after subtraction = ", a)
Here is a Rust program that runs, but I cannot get the closures to update the so-called global variable.
fn fmain() {
// global variable
let mut a = 15;
// perform addition
let add = || {
let mut _name = a;
// name += 100; // the program won't compile if this is uncommented
};
call_once(add);
// perform subtraction
let subtract = || {
let mut _name = a;
// name -= 100; // the program won't compile if this is uncommented
};
call_once(subtract);
// Using a global through functions
println!("Initial value of a = {}", a);
add();
println!("a after addition = {}", a);
subtract();
println!("a after subtraction = {}", a);
}
fn main() {
fmain();
}
fn call_once<F>(f: F)
where
F: FnOnce(),
{
f();
}
My request: Re-create the Python logic in Rust.
Your Rust code is not using global variables, the a variable is stack-allocated. While Rust doesn't particularly endorse global variables, you can certainly use them. Translated to Rust that uses actual globals, your program would look like this:
use lazy_static::lazy_static;
use parking_lot::Mutex; // or std::sync::Mutex
// global variable
lazy_static! {
static ref A: Mutex<u32> = Mutex::new(15);
}
// function to perform addition
fn add() {
*A.lock() += 100;
}
// function to perform subtraction
fn subtract() {
*A.lock() -= 100;
}
fn main() {
// Using a global through functions
println!("Initial value of a = {}", A.lock());
add();
println!("a after addition = {}", A.lock());
subtract();
println!("a after subtraction = {}", A.lock());
}
Playground
If you prefer to use closures, you can do that too, but you'll need to use interior mutability to allow multiple closures to capture the same environment. For example, you could use a Cell:
use std::cell::Cell;
fn main() {
let a = Cell::new(15);
let add = || {
a.set(a.get() + 100);
};
let subtract = || {
a.set(a.get() - 100);
};
// Using a global through functions
println!("Initial value of a = {}", a.get());
add();
println!("a after addition = {}", a.get());
subtract();
println!("a after subtraction = {}", a.get());
}
Playground
Dependency-less examples as enum and function. EDIT : Code improved, as suggested in comment and corrected match arm.
use std::sync::{Arc, Mutex, Once};
static START: Once = Once::new();
static mut ARCMUT: Vec<Arc<Mutex<i32>>> = Vec::new();
// as enum
enum Operation {
Add,
Subtract,
}
impl Operation {
// static change
fn result(self) -> i32 {
let mut arc_clone = unsafe { ARCMUT[0].clone() };
let mut unlock = arc_clone.lock().unwrap();
match self {
Operation::Add => *unlock += 100,
Operation::Subtract => *unlock -= 100,
}
*unlock
}
// dynamic change
fn amount(self, amount: i32) -> i32 {
let mut arc_clone = unsafe { ARCMUT[0].clone() };
let mut unlock = arc_clone.lock().unwrap();
match self {
Operation::Add => *unlock += amount,
Operation::Subtract => *unlock -= amount,
}
*unlock
}
}
// as a function
fn add() -> i32 {
let mut arc_clone = unsafe { ARCMUT[0].clone() };
let mut unlcok = arc_clone.lock().unwrap();
*unlcok += 100;
*unlcok
}
// as trait
trait OperationTrait {
fn add(self) -> Self;
fn subtract(self) -> Self;
fn return_value(self) ->i32;
}
impl OperationTrait for i32 {
fn add(mut self) -> Self {
let arc_clone = unsafe{ARCMUT[0].clone()};
let mut unlock = arc_clone.lock().unwrap();
*unlock += self;
self
}
fn subtract(mut self) -> Self {
let arc_clone = unsafe{ARCMUT[0].clone()};
let mut unlock = arc_clone.lock().unwrap();
*unlock -= self;
self
}
fn return_value(self)->Self{
let arc_clone = unsafe{ARCMUT[0].clone()};
let mut unlock = arc_clone.lock().unwrap();
*unlock
}
}
// fn main
fn main() {
START.call_once(|| unsafe {
ARCMUT = vec![Arc::new(Mutex::new(15))];
});
let test = Operation::Add.result();
println!("{:?}", test);
let test = Operation::Subtract.amount(100);
println!("{:?}", test);
let test = add();
println!("{:?}", test);
let test = 4000.add();
println!("{:?}", test);
}

How do I use a Condvar to limit multithreading?

I'm trying to use a Condvar to limit the number of threads that are active at any given time. I'm having a hard time finding good examples on how to use Condvar. So far I have:
use std::sync::{Arc, Condvar, Mutex};
use std::thread;
fn main() {
let thread_count_arc = Arc::new((Mutex::new(0), Condvar::new()));
let mut i = 0;
while i < 100 {
let thread_count = thread_count_arc.clone();
thread::spawn(move || {
let &(ref num, ref cvar) = &*thread_count;
{
let mut start = num.lock().unwrap();
if *start >= 20 {
cvar.wait(start);
}
*start += 1;
}
println!("hello");
cvar.notify_one();
});
i += 1;
}
}
The compiler error given is:
error[E0382]: use of moved value: `start`
--> src/main.rs:16:18
|
14 | cvar.wait(start);
| ----- value moved here
15 | }
16 | *start += 1;
| ^^^^^ value used here after move
|
= note: move occurs because `start` has type `std::sync::MutexGuard<'_, i32>`, which does not implement the `Copy` trait
I'm entirely unsure if my use of Condvar is correct. I tried staying as close as I could to the example on the Rust API. Wwat is the proper way to implement this?
Here's a version that compiles:
use std::{
sync::{Arc, Condvar, Mutex},
thread,
};
fn main() {
let thread_count_arc = Arc::new((Mutex::new(0u8), Condvar::new()));
let mut i = 0;
while i < 100 {
let thread_count = thread_count_arc.clone();
thread::spawn(move || {
let (num, cvar) = &*thread_count;
let mut start = cvar
.wait_while(num.lock().unwrap(), |start| *start >= 20)
.unwrap();
// Before Rust 1.42, use this:
//
// let mut start = num.lock().unwrap();
// while *start >= 20 {
// start = cvar.wait(start).unwrap()
// }
*start += 1;
println!("hello");
cvar.notify_one();
});
i += 1;
}
}
The important part can be seen from the signature of Condvar::wait_while or Condvar::wait:
pub fn wait_while<'a, T, F>(
&self,
guard: MutexGuard<'a, T>,
condition: F
) -> LockResult<MutexGuard<'a, T>>
where
F: FnMut(&mut T) -> bool,
pub fn wait<'a, T>(
&self,
guard: MutexGuard<'a, T>
) -> LockResult<MutexGuard<'a, T>>
This says that wait_while / wait consumes the guard, which is why you get the error you did - you no longer own start, so you can't call any methods on it!
These functions are doing a great job of reflecting how Condvars work - you give up the lock on the Mutex (represented by start) for a while, and when the function returns you get the lock again.
The fix is to give up the lock and then grab the lock guard return value from wait_while / wait. I've also switched from an if to a while, as encouraged by huon.
For reference, the usual way to have a limited number of threads in a given scope is with a Semaphore.
Unfortunately, Semaphore was never stabilized, was deprecated in Rust 1.8 and was removed in Rust 1.9. There are crates available that add semaphores on top of other concurrency primitives.
let sema = Arc::new(Semaphore::new(20));
for i in 0..100 {
let sema = sema.clone();
thread::spawn(move || {
let _guard = sema.acquire();
println!("{}", i);
})
}
This isn't quite doing the same thing: since each thread is not printing the total number of the threads inside the scope when that thread entered it.
I realized the code I provided didn't do exactly what I wanted it to, so I'm putting this edit of Shepmaster's code here for future reference.
use std::sync::{Arc, Condvar, Mutex};
use std::thread;
fn main() {
let thread_count_arc = Arc::new((Mutex::new(0u8), Condvar::new()));
let mut i = 0;
while i < 150 {
let thread_count = thread_count_arc.clone();
thread::spawn(move || {
let x;
let &(ref num, ref cvar) = &*thread_count;
{
let start = num.lock().unwrap();
let mut start = if *start >= 20 {
cvar.wait(start).unwrap()
} else {
start
};
*start += 1;
x = *start;
}
println!("{}", x);
{
let mut counter = num.lock().unwrap();
*counter -= 1;
}
cvar.notify_one();
});
i += 1;
}
println!("done");
}
Running this in the playground should show more or less expected behavior.
You want to use a while loop, and re-assign start at each iteration, like:
fn main() {
let thread_count_arc = Arc::new((Mutex::new(0), Condvar::new()));
let mut i = 0;
while i < 100 {
let thread_count = thread_count_arc.clone();
thread::spawn(move || {
let &(ref num, ref cvar) = &*thread_count;
let mut start = num.lock().unwrap();
while *start >= 20 {
let current = cvar.wait(start).unwrap();
start = current;
}
*start += 1;
println!("hello");
cvar.notify_one();
});
i += 1;
}
}
See also some article on the topic:
https://medium.com/#polyglot_factotum/rust-concurrency-five-easy-pieces-871f1c62906a
https://medium.com/#polyglot_factotum/rust-concurrency-patterns-condvars-and-locks-e278f18db74f

Several managed closures with shared state?

I am trying to implement a Rust wrapper for Expat XML parser. I wrapped the start_element, end_element callbacks and they work fine in simple cases (e.g. just counting XML elements) as follows:
struct Expat {
parser: expat::XML_Parser
}
type StartHandler = #fn(tag: &str, attrs: &[~str]);
type EndHandler = #fn(tag: &str);
type TextHandler = #fn(text: &str);
struct Handlers {
start_handler: StartHandler,
end_handler: EndHandler,
text_handler: TextHandler
}
impl Expat {
pub fn handlers(&self, start_handler: StartHandler, end_handler: EndHandler, text_handler: TextHandler) {
let handlers = #Handlers {
start_handler: start_handler,
end_handler: end_handler,
text_handler: text_handler
};
// How to do this properly?
unsafe { cast::bump_box_refcount(handlers) };
expat::XML_SetUserData(self.parser, unsafe { cast::transmute(&*handlers) });
}
I can pass simple managed closures to handlers() and have them update #mut uint values.
Now I want to maintain the current XPath across callbacks and am having problems:
let mut xpath: ~[~str] = ~[];
let xpath_start_handler: #fn(&str, &[~str]) = |tag: &str, _attrs: &[~str]| {
vec::push(&xpath, tag.to_owned());
println(fmt!(" start: %?", xpath));
};
let xpath_end_handler: #fn(&str) = |tag: &str| {
println(fmt!(" end: %?", xpath));
let top = vec::pop(&xpath);
if top != tag.to_owned() {
fail!(fmt!("expected end tag: %s, received end tag: %s", top, tag));
}
};
let xpath_text_handler: #fn(&str) = |_text: &str| {
};
expat.handlers(xpath_start_handler, xpath_end_handler, xpath_text_handler);
The compiler says that the unique vector xpath was moved into the xpath_start_handler closure and cannot be accessed in xpath_end_closure.
So my question is what is the best way to maintain mutable state across many managed closures?
Shared boxes should be managed, not unique:
let state: #mut ~[~str] = #mut ~[];
let push: #fn(~str) = |x| {
vec::push(state, x);
};
let pop: #fn() -> ~str = || {
vec::pop(state)
};
let count: #fn() -> uint = || {
(&*state).len()
};
push(~"ho");
push(~"hey");
println(fmt!("%?", count()));
println(pop());
println(pop());
println(fmt!("%?", count()));
Also, mutable unique boxes work a bit differently than mutable managed boxes.

Resources