How to recover the concrete type from Any - casting

Code:
use std::fmt::Debug;
use std::any::Any;
fn any_to_u16(value: &dyn Any)
{
let v = value as u16;
}
fn main()
{
let x = true;
any_to_u16(&x);
}
Erorr :
error[E0606]: casting `&(dyn std::any::Any + 'static)` as `u16` is invalid
--> src/lib.rs:6:13
|
6 | let v = value as u16;
| ^^^^^^^^^^^^
|
= help: cast through a raw pointer first
Playground
How to fix?

You must use Any::downcast_ref:
use std::any::Any;
fn any_to_u16(value: &dyn Any)
{
if let Some(value) = value.downcast_ref::<bool>().map(|b| *b as u16) {
// value is a `bool`
assert_eq!(value, 1);
}
}
fn main()
{
let x = true;
any_to_u16(&x);
}

Related

Unit testing a service that accepts an Fn closure as a callback

I have the following service that registers callbacks to execute at a certain epoch, identified by an i64. The service has a vector of callbacks (that are bounded by the Send + Fn() -> () traits). Each callback can be executed multiple times (hence Fn instead of FnOnce or FnMut). The Send trait is needed because the callbacks will be registered by other threads, and this service will run in the background.
So far so good, but I'd like to test that the callbacks are executed the way they should be (i.e. the i64 epoch ticking in some direction which may (or may not) cause the callback to be executed). The problem is that I cannot seem to be able to think of a way to achieve this. I'm coming from Golang in which it is quite easy to inject a mock callback and assert whether it was called since such limitations are not imposed by the compiler, however when I employ the same methods in Rust, I end up with an FnMut instead of an Fn.
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
struct Service<T: Send + Fn() -> ()> {
triggers: Arc<Mutex<HashMap<i64, Vec<Box<T>>>>>,
}
impl<T: Send + Fn() -> ()> Service<T> {
pub fn build() -> Self {
Service {
triggers: Arc::new(Mutex::new(HashMap::new())),
}
}
pub fn poll(&'static self) {
let hs = Arc::clone(&self.triggers);
tokio::spawn(async move {
loop {
// do some stuff and get `val`
if let Some(v) = hs.lock().unwrap().get(&val) {
for cb in v.iter() {
cb();
}
}
}
});
()
}
pub fn register_callback(&self, val: i64, cb: Box<T>) -> () {
self.triggers
.lock()
.unwrap()
.entry(val)
.or_insert(Vec::new())
.push(cb);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_poll() {
let c = Service::build();
let mut called = false;
let cb = || called = true;
let h: i64 = 10;
c.register_callback(h, Box::new(cb));
assert_eq!(called, false);
}
}
Any ideas on how would this sort of behavior could be tested in Rust? The only thing I can think of is perhaps some channel that would pass a local value to the test and relinquish ownership over it?
The best way would probably be to make your interface as general as possible:
// type bounds on structs are generally unnecessary so I removed it here.
struct Service<T> {
triggers: Arc<Mutex<HashMap<i64, Vec<Box<T>>>>>,
}
impl<T: Send + FnMut() -> ()> Service<T> {
pub fn build() -> Self {
Service {
triggers: Arc::new(Mutex::new(HashMap::new())),
}
}
pub fn poll(&'static self, val: i64) {
let hs = Arc::clone(&self.triggers);
tokio::spawn(async move {
loop {
// do some stuff and get `val`
if let Some(v) = hs.lock().unwrap().get_mut(&val) {
for cb in v.iter_mut() {
cb();
}
}
}
});
()
}
pub fn register_callback(&self, val: i64, cb: Box<T>) -> () {
self.triggers
.lock()
.unwrap()
.entry(val)
.or_insert(Vec::new())
.push(cb);
}
}
But if you can't generalize the interface you can just use an AtomicBool like this:
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::{Ordering, AtomicBool};
#[test]
fn test_poll() {
let c = Service::build();
let mut called = AtomicBool::new(false);
let cb = || called.store(true, Ordering::Relaxed);
let h: i64 = 10;
c.register_callback(h, Box::new(cb));
assert!(!called.load(Ordering::Relaxed));
}
}

Mocking functions in rust

Is there a way to mock regular functions in rust?
Consider the following code:
fn main() {
println!("{}", foo());
}
fn get_user_input() -> u8 {
// Placeholder for some unknown value
42
}
fn foo() -> u8 {
get_user_input()
}
#[cfg(test)]
mod tests {
#[test]
fn test_foo() {
use super::*;
get_user_input = || 12u8;
assert_eq!(foo(), 12u8);
}
}
I would like to unit test foo() without having to rely on the output of get_user_input().
I obviously cannot overwrite get_user_input() like I tried in the example code.
I have only found ways to mock structs, traits and modules but nothing about mocking regular free functions. Am I missing something?
Edit: I have looked primarily at the mockall crate.
You could use cfg:
#[cfg(not(test))]
fn get_user_input() -> u8 {
// Placeholder for some unknown value
42
}
#[cfg(test)]
fn get_user_input() -> u8 {
12
}
playground
Or dependency injection:
pub fn main() {
println!("{}", foo(get_user_input));
}
fn get_user_input() -> u8 {
// Placeholder for some unknown value
42
}
fn foo(get_user_input_: impl Fn() -> u8) -> u8 {
get_user_input_()
}
#[cfg(test)]
mod tests {
#[test]
fn test_foo() {
use super::*;
let get_user_input = || 12u8;
assert_eq!(foo(get_user_input), 12u8);
}
}
playgound

Problem calling C++ Class method from Rust

The following code is generated by bindgen.
extern "C" {
#[doc = "MoraComm Properties"]
#[link_name = "\u{1}_ZN22MoraCommManagerWrapped10propertiesEj"]
pub fn MoraCommManagerWrapped_properties(
this: *mut MoraCommManagerWrapped,
deviceNumber: ::std::os::raw::c_uint,
) -> MoraCommPropertiesWrapped;
}
impl MoraCommManagerWrapped {
#[inline]
pub unsafe fn properties(
&mut self,
deviceNumber: ::std::os::raw::c_uint,
) -> MoraCommPropertiesWrapped {
MoraCommManagerWrapped_properties(self, deviceNumber)
}
#[inline]
pub unsafe fn new() -> Self {
let mut __bindgen_tmp = ::std::mem::MaybeUninit::uninit();
MoraCommManagerWrapped_MoraCommManagerWrapped(__bindgen_tmp.as_mut_ptr());
__bindgen_tmp.assume_init()
}
}
fn main() {
unsafe {
let mut mgr: MoraCommManagerWrapped = MoraCommManagerWrapped::new();
let p = mgr.properties(0); // <- segfault
}
}
When main is run, there is a segfault.
I added logging on the C++ side, that shows that the value of the this pointer is equal to value of the deviceNumber arg.
I call another class that is part of the same bindgen generated bindings and it works.
Any thoughts on what might be going on?

Designing unit tests when `Result` is not `Copy`

I have a unit test for a container type I'm working on implementing:
#[test]
fn test_get_mut_normal_tail() -> Result<(), ListError> {
let mut actual_list: ArrayList<u64> = ArrayList::new();
let expected_list: ArrayList<u64> = ArrayList {
elems: vec![1, 2, 3, 8],
};
actual_list.append(1)?;
actual_list.append(2)?;
actual_list.append(3)?;
actual_list.append(4)?;
let actual_res: Result<&mut u64, ListError> = actual_list.get_mut(3);
let expected_res: Result<&mut u64, ListError> = Ok(&mut 4);
let elem = actual_res.unwrap();
*elem *= 2;
assert_eq!(actual_list, expected_list);
assert_eq!(actual_res, expected_res);
Ok(())
}
However, rustc complains with:
error[E0382]: borrow of moved value: `actual_res`
--> src\arraylist.rs:358:9
|
351 | let actual_res: Result<&mut u64, ListError> = actual_list.get_mut(3);
| ---------- move occurs because `actual_res` has type `std::result::Result<&mut u64, list::ListError>`, which does not implement the `Copy` trait
...
354 | let elem = actual_res.unwrap();
| ---------- value moved here
...
358 | assert_eq!(actual_res, expected_res);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ value borrowed here after move
|
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
Which I don't understand, because the Result docs indicate that Result implements Copy (provided both of the contained types do also). Clearly &mut u64 implements Copy and my ListError type derives it also:
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum ListError {
OutOfBounds,
Impossible,
}
The code for ArrayList::get_mut() is here, if relevant:
fn get_mut(&mut self, pos: usize) -> Result<&mut T, ListError> {
if pos >= self.elems.len() { /* bounds check */
return Err(ListError::OutOfBounds);
}
match self.elems.get_mut(pos) {
Some(elem) => Ok(elem),
None => Err(ListError::OutOfBounds)
}
}
In summary, I have two questions:
How do I fix this error?
Is there an underlying issue in either my test or my implementation?
&mut u64 does not actually implement copy. &mut T cannot implement copy to prevent creating multiple simultaneous mutable borrows.
Documentation for reference: https://doc.rust-lang.org/std/primitive.reference.html#trait-implementations
In answer to 1, you can fix the error by using assert_eq!(elem, 4); instead of assert_eq!(actual_res, expected_res);. Or you could use Result::map to modify and return a new result
let actual_res: Result<&mut u64, ListError> = actual_list.get_mut(3).map(|elem| {
*elem *= 2;
elem
};

How do I get the X window class given a window ID with rust-xcb?

I'm trying to use rust-xcb to get a window's class given a window ID.
fn get_class(conn: &xcb::Connection, id: &i32) {
let window: xcb::xproto::Window = *id as u32;
let class_prop: xcb::xproto::Atom = 67; // XCB_ATOM_WM_CLASS from xproto.h
let cookie = xcb::xproto::get_property(&conn, false, window, class_prop, 0, 0, 2);
match cookie.get_reply() {
Ok(reply) => {
let x: &[std::os::raw::c_void] = reply.value();
println!("reply is {:?}", x[0]);
}
Err(err) => println!("err {:?}", err),
}
}
The documentation is kind of sparse and hasn't been incredibly helpful, though I did find this bit about the GetPropertyReply and of the xcb_get_property_reply_t it wraps.
I looked at this answer in JavaScript but I don't know what the ctypes equivalent in Rust is. I tried just casting the &[c_void] as a &str or String:
...
Ok(reply) => {
let len = reply.value_len() as usize;
let buf = reply.value() as &str;
println!("{}", buf.slice_unchecked(0, len)); // this seems redundant
}
...
but it returns
error: non-scalar cast: `&[_]` as `&str`
I tried casting the &[c_void] as a &[u8] and then collecting the Vec into a String, which sort of works:
...
Ok(reply) => {
let value : &[u8] = reply.value();
let buf : String = value.into_iter().map(|i| *i as char).collect();
println!("\t{:?}", buf);
}
...
but I'm now getting weird results. for example , when I use xprop on Chrome I see "google-chrome" but for me it is only showing "google-c", and "roxterm" is showing up as "roxterm\u{0}". I'm guessing "\u{0}" is something Unicode related but I'm not sure, and I don't know why stuff is being concatenated either. Maybe I have to check the reply again?
Here's my updated function:
fn get_class(conn: &Connection, id: &i32) -> String {
let window: xproto::Window = *id as u32;
let long_length: u32 = 8;
let mut long_offset: u32 = 0;
let mut buf = Vec::new();
loop {
let cookie = xproto::get_property(
&conn,
false,
window,
xproto::ATOM_WM_CLASS,
xproto::ATOM_STRING,
long_offset,
long_length,
);
match cookie.get_reply() {
Ok(reply) => {
let value: &[u8] = reply.value();
buf.extend_from_slice(value);
match reply.bytes_after() {
0 => break,
_ => {
let len = reply.value_len();
long_offset += len / 4;
}
}
}
Err(err) => {
println!("{:?}", err);
break;
}
}
}
let result = String::from_utf8(buf).unwrap();
let results: Vec<&str> = result.split('\0').collect();
results[0].to_string()
}
There were three main parts to this question:
I put xproto::get_property() in a loop so I could check reply.bytes_after() and accordingly adjust long_offset. I think with an appropriate long_length there will usually only be one read, but just being safe.
As #peter-hall said, converting &[u8] -> String should be done using String::from_utf8, which needs a Vec; so I let mut buf = Vec::new() and buf.extend_from_slice over the loop before creating the result string with String::from_utf8(buf).unwrap()
According to this random page WM_CLASS is actually two consecutive null-terminated strings, so I split the result by \0 and grab the first value.
I might've just been looking in the wrong place, but xcb has absolutely terrible documentation..