How can I map Result<(), E> into a numeric error code? - rust

I have a bunch of FFI functions that I call using C. The caller expects 1 for success, or -1 on failure.
struct Error;
fn my_rust_function() -> Result<(), Error> {
Ok(())
}
#[allow(non_snake_case)]
pub extern "C" fn Called_From_C() -> i32 {
let result = my_rust_function();
match result {
Ok(_) => 1,
Err(_) => -1,
}
}
Is there a more idiomatic way of converting my Result<(), Error> into the 1 / -1 return code?

I'd create an extension trait:
trait FfiError {
fn as_c_error(&self) -> i32;
}
impl<T, E> FfiError for Result<T, E> {
fn as_c_error(&self) -> i32 {
match self {
Ok(_) => 1,
Err(_) => -1,
}
}
}
Once it's brought into scope, you can call it like any other method:
pub extern "C" fn called_from_c() -> i32 {
my_rust_function().as_c_error()
}
See also:
Is there a way other than traits to add methods to a type I don't own?

You could use repr(transparent) to create a type where you could implement From and that still represent a i32, this allow to compile check that you transform your result correctly assuming you didn't have bug in your from() implementation so maybe add some unit tests.
type MyResult = Result<(), ()>;
fn my_rust_function() -> MyResult {
Ok(())
}
#[repr(transparent)]
pub struct CResult {
code: i32,
// code: libc::c_int, // if your C lib expect a `c_int` and not a `i32`
}
impl From<MyResult> for CResult {
fn from(result: MyResult) -> Self {
let code = match result {
Ok(_) => 1,
Err(_) => -1,
};
Self { code }
}
}
#[allow(non_snake_case)]
pub extern "C" fn Called_From_C() -> CResult {
let result = my_rust_function();
result.into()
}
You could also use enum with repr(i32):
#[repr(i32)]
pub enum CResult {
NoError = 1,
Error = -1,
}
impl From<MyResult> for CResult {
fn from(result: MyResult) -> Self {
match result {
Ok(_) => CResult::NoError,
Err(_) => CResult::Error,
}
}
}
In nightly, you could also implement Try:
#![feature(try_trait)]
use std::ops::Try;
type MyResult = Result<(), ()>;
fn my_rust_function() -> MyResult {
Ok(())
}
#[repr(i32)]
pub enum CResult {
NoError = 1,
Error = -1,
}
impl From<MyResult> for CResult {
fn from(result: MyResult) -> Self {
match result {
Ok(_) => CResult::NoError,
Err(_) => CResult::Error,
}
}
}
impl From<CResult> for MyResult {
fn from(cresult: CResult) -> Self {
match cresult {
CResult::NoError => Ok(()),
CResult::Error => Err(()),
}
}
}
impl Try for CResult {
type Ok = ();
type Error = ();
fn into_result(self) -> MyResult {
self.into()
}
fn from_ok(_: <Self as Try>::Ok) -> Self {
Self::NoError
}
fn from_error(_: <Self as Try>::Error) -> Self {
Self::Error
}
}
#[allow(non_snake_case)]
pub extern "C" fn Called_From_C() -> CResult {
let _ = my_rust_function()?;
CResult::NoError
}
Note: Be careful with the enumeration one, make sure your implementation is compatible. #[repr(libc::c_int)] is what we really want but I don't know any way to express this in Rust. So maybe a structure with repr(transparent) is more safe if the lib expect a c_int.

Related

Heterogeneous collection as a member of a class in Rust

I am new to Rust, and does not fully understand lifetime, so probably, that is why I can't solv the following issue. I need a solution in which a class has a heterogeneous HashMap containing different objects derived from the same trait.
I have to be able to extend an object with some (multiple) functionality dinamically. Other solutions are also welcome. Adding functionality to the class in compile time could also work, but adding functionality directly to the main class not.
use std::collections::HashMap;
trait DoerTrait {
fn do_something( & self, a : u8, b : u8 ) -> u8;
}
struct MyDoer<'a> {
}
impl DoerTrait for MyDoer<'a> {
fn do_something( & self, a : u8, b : u8 ) -> u8 {
return a + b;
}
}
struct MyMain<'a> {
doers : HashMap<u8,&'a dyn DoerTrait>,
}
impl<'a> MyMain<'a> {
fn new() -> Self {
Self {
doers : HashMap::new()
}
}
fn add_doer( &mut self, id : u8, doer : & dyn DoerTrait ) {
self.doers.insert( id, doer );
}
fn do_something( & self, id : u8 ) {
match self.doers.get( &id ) {
Some( doer ) => {
println!( "{}", doer(19,26) );
}
None => {
println!( "Doer not specified!" );
}
}
}
}
fn main() {
let mut mymain = MyMain::new();
let mydoer = MyDoer{};
mymain.add_doer( 42, &mydoer );
mymain.do_something( 42 );
}
Not too sure what issue you have, once MyDoer has been stripped of its incorrect (unnecessary) lifetime and the lifetime has correctly been declared on impl MyMain, the compiler directly points to the parameter of add_doer not matching (after which it points out that doer in do_something is not a function):
use std::collections::HashMap;
trait DoerTrait {
fn do_something(&self, a: u8, b: u8) -> u8;
}
struct MyDoer;
impl DoerTrait for MyDoer {
fn do_something(&self, a: u8, b: u8) -> u8 {
return a + b;
}
}
struct MyMain<'a> {
doers: HashMap<u8, &'a dyn DoerTrait>,
}
impl<'a> MyMain<'a> {
fn new() -> Self {
Self {
doers: HashMap::new(),
}
}
fn add_doer(&mut self, id: u8, doer: &'a dyn DoerTrait) {
self.doers.insert(id, doer);
}
fn do_something(&self, id: u8) {
match self.doers.get(&id) {
Some(doer) => {
println!("{}", doer.do_something(19, 26));
}
None => {
println!("Doer not specified!");
}
}
}
}
fn main() {
let mut mymain = MyMain::new();
let mydoer = MyDoer {};
mymain.add_doer(42, &mydoer);
mymain.do_something(42);
}

How to create a single threaded singleton in Rust?

I'm currently trying to wrap a C library in rust that has a few requirements. The C library can only be run on a single thread, and can only be initialized / cleaned up once on the same thread. I want something something like the following.
extern "C" {
fn init_lib() -> *mut c_void;
fn cleanup_lib(ctx: *mut c_void);
}
// This line doesn't work.
static mut CTX: Option<(ThreadId, Rc<Context>)> = None;
struct Context(*mut c_void);
impl Context {
fn acquire() -> Result<Rc<Context>, Error> {
// If CTX has a reference on the current thread, clone and return it.
// Otherwise initialize the library and set CTX.
}
}
impl Drop for Context {
fn drop(&mut self) {
unsafe { cleanup_lib(self.0); }
}
}
Anyone have a good way to achieve something like this? Every solution I try to come up with involves creating a Mutex / Arc and making the Context type Send and Sync which I don't want as I want it to remain single threaded.
A working solution I came up with was to just implement the reference counting myself, removing the need for Rc entirely.
#![feature(once_cell)]
use std::{error::Error, ffi::c_void, fmt, lazy::SyncLazy, sync::Mutex, thread::ThreadId};
extern "C" {
fn init_lib() -> *mut c_void;
fn cleanup_lib(ctx: *mut c_void);
}
#[derive(Debug)]
pub enum ContextError {
InitOnOtherThread,
}
impl fmt::Display for ContextError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
ContextError::InitOnOtherThread => {
write!(f, "Context already initialized on a different thread")
}
}
}
}
impl Error for ContextError {}
struct StaticPtr(*mut c_void);
unsafe impl Send for StaticPtr {}
static CTX: SyncLazy<Mutex<Option<(ThreadId, usize, StaticPtr)>>> =
SyncLazy::new(|| Mutex::new(None));
pub struct Context(*mut c_void);
impl Context {
pub fn acquire() -> Result<Context, ContextError> {
let mut ctx = CTX.lock().unwrap();
if let Some((id, ref_count, ptr)) = ctx.as_mut() {
if *id == std::thread::current().id() {
*ref_count += 1;
return Ok(Context(ptr.0));
}
Err(ContextError::InitOnOtherThread)
} else {
let ptr = unsafe { init_lib() };
*ctx = Some((std::thread::current().id(), 1, StaticPtr(ptr)));
Ok(Context(ptr))
}
}
}
impl Drop for Context {
fn drop(&mut self) {
let mut ctx = CTX.lock().unwrap();
let (_, ref_count, ptr) = ctx.as_mut().unwrap();
*ref_count -= 1;
if *ref_count == 0 {
unsafe {
cleanup_lib(ptr.0);
}
*ctx = None;
}
}
}
I think the most 'rustic' way to do this is with std::sync::mpsc::sync_channel and an enum describing library operations.
The only public-facing elements of this module are launch_lib(), the SafeLibRef struct (but not its internals), and the pub fn that are part of the impl SafeLibRef.
Also, this example strongly represents the philosophy that the best way to deal with global state is to not have any.
I have played fast and loose with the Result::unwrap() calls. It would be more responsible to handle error conditions better.
use std::sync::{ atomic::{ AtomicBool, Ordering }, mpsc::{ SyncSender, Receiver, sync_channel } };
use std::ffi::c_void;
extern "C" {
fn init_lib() -> *mut c_void;
fn do_op_1(ctx: *mut c_void, a: u16, b: u32, c: u64) -> f64;
fn do_op_2(ctx: *mut c_void, a: f64) -> bool;
fn cleanup_lib(ctx: *mut c_void);
}
enum LibOperation {
Op1(u16,u32,u64,SyncSender<f64>),
Op2(f64, SyncSender<bool>),
Terminate(SyncSender<()>),
}
#[derive(Clone)]
pub struct SafeLibRef(SyncSender<LibOperation>);
fn lib_thread(rx: Receiver<LibOperation>) {
static LIB_INITIALIZED: AtomicBool = AtomicBool::new(false);
if LIB_INITIALIZED.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst).is_err() {
panic!("Tried to double-initialize library!");
}
let libptr = unsafe { init_lib() };
loop {
let op = rx.recv();
if op.is_err() {
unsafe { cleanup_lib(libptr) };
break;
}
match op.unwrap() {
LibOperation::Op1(a,b,c,tx_res) => {
let res: f64 = unsafe { do_op_1(libptr, a, b, c) };
tx_res.send(res).unwrap();
},
LibOperation::Op2(a, tx_res) => {
let res: bool = unsafe { do_op_2(libptr, a) };
tx_res.send(res).unwrap();
}
LibOperation::Terminate(tx_res) => {
unsafe { cleanup_lib(libptr) };
tx_res.send(()).unwrap();
break;
}
}
}
}
/// This needs to be called no more than once.
/// The resulting SafeLibRef can be cloned and passed around.
pub fn launch_lib() -> SafeLibRef {
let (tx,rx) = sync_channel(0);
std::thread::spawn(|| lib_thread(rx));
SafeLibRef(tx)
}
// This is the interface that most of your code will use
impl SafeLibRef {
pub fn op_1(&self, a: u16, b: u32, c: u64) -> f64 {
let (res_tx, res_rx) = sync_channel(1);
self.0.send(LibOperation::Op1(a, b, c, res_tx)).unwrap();
res_rx.recv().unwrap()
}
pub fn op_2(&self, a: f64) -> bool {
let (res_tx, res_rx) = sync_channel(1);
self.0.send(LibOperation::Op2(a, res_tx)).unwrap();
res_rx.recv().unwrap()
}
pub fn terminate(&self) {
let (res_tx, res_rx) = sync_channel(1);
self.0.send(LibOperation::Terminate(res_tx)).unwrap();
res_rx.recv().unwrap();
}
}

How to downcast Rc<RefCell<dyn io::Write>> into a concrete type?

I want to make a struct which the text output can be either displayed on the console or stored in an internal buffer. If the text is buffered, then I need a method that gives back the text content.
For this aim I use a property named writer, which is dyn std::io::Write (wrapped into Rc<RefCell<>> because it is needed by my real code). Then on struct construction, I create either a io::stdout() instance or a Vec::<u8>::new() instance for this property.
use std::rc::Rc;
use std::cell::RefCell;
use std::io;
struct A {
// Rc<RefCell<>> is needed in my real code
writer: Rc<RefCell<dyn io::Write>>,
}
impl A {
pub fn new() -> Self {
Self { writer: Rc::new(RefCell::new(io::stdout())) }
}
pub fn new_buffered() -> Self {
Self { writer: Rc::new(RefCell::new(Vec::<u8>::new())) }
}
pub fn write(&self, s: &str) {
let mut writer = self.writer.borrow_mut();
writeln!(writer, "{}", s).unwrap();
}
/// Returns None if the struct is not buffered, otherwise a copy of the buffered output.
pub fn get_buffer(&self) -> Option<String> {
match GET_VEC_U8() { // <- Unable to implement this line
Some(vec_u8) => {
Some(String::from_utf8(vec_u8.clone()).unwrap())
},
None => None,
}
}
}
fn main() {
let a = A::new();
a.write("foo");
println!("Buffer: {:?}", a.get_buffer());
let b = A::new_buffered();
b.write("bar");
println!("Buffer: {:?}", b.get_buffer());
}
Question
But I can't figure out how to extract the text content (method get_buffer()), when the writer is Vec<u8>. How can I do it ?
My try
I tried to wrap the property into a Box:
struct A {
writer: Rc<RefCell<Box<dyn io::Write>>>,
}
then use Box::downcast() on it:
impl A {
pub fn get_buffer(&self) -> Option<String> {
let writer = self.writer.borrow();
match (*writer).downcast::<Vec<u8>>() {
Ok(vec_u8) => Some(String::from_utf8(vec_u8.clone()).unwrap()),
Err(_) => None,
}
}
}
but I get this error:
error[E0599]: no method named `downcast` found for struct `std::boxed::Box<dyn std::io::Write>` in the current scope
--> src/main.rs:27:25
|
27 | match (*writer).downcast::<Vec<u8>>() {
| ^^^^^^^^ method not found in `std::boxed::Box<dyn std::io::Write>`
As #SvenMarnach wrote in the comments, writing a custom trait depending on io::Write can be a solution
use std::rc::Rc;
use std::cell::RefCell;
use std::io::{self, Stdout};
trait MyWrite: io::Write {
fn get_buffer(&self) -> Option<String>;
}
impl MyWrite for Stdout {
fn get_buffer(&self) -> Option<String> {
None
}
}
impl MyWrite for Vec<u8> {
fn get_buffer(&self) -> Option<String> {
Some(String::from_utf8(self.clone()).unwrap())
}
}
struct A {
// Rc<RefCell<>> is needed in my real code
writer: Rc<RefCell<dyn MyWrite>>,
}
impl A {
pub fn new() -> Self {
Self { writer: Rc::new(RefCell::new(io::stdout())) }
}
pub fn new_buffered() -> Self {
Self { writer: Rc::new(RefCell::new(Vec::<u8>::new())) }
}
pub fn write(&self, s: &str) {
let mut writer = self.writer.borrow_mut();
writeln!(writer, "{}", s).unwrap();
}
/// Returns None if the struct is not buffered, otherwise a copy of the buffered output.
pub fn get_buffer(&self) -> Option<String> {
let writer = self.writer.borrow();
writer.get_buffer()
}
}
fn main() {
let a = A::new();
a.write("foo");
println!("Buffer: {:?}", a.get_buffer());
let b = A::new_buffered();
b.write("bar");
println!("Buffer: {:?}", b.get_buffer());
}

Why can I not dereference or add a generic type even though I'm passing in a type that can be dereferenced and added?

I have some problems with a generic implementation of a method:
use std::collections::BTreeMap;
use global::entity::EntityId;
struct simple_system<T> {
list_sum: BTreeMap<EntityId, T>,
list_com: BTreeMap<EntityId, Vec<T>>,
}
impl<T> simple_system<T> {
pub fn new() -> simple_system<T> {
simple_system {
list_sum: BTreeMap::new(),
list_com: BTreeMap::new(),
}
}
pub fn addComponent(&mut self, id: EntityId, comp: T) {
self.list_com.entry(id).or_insert_with(Vec::new).push(comp);
match self.list_sum.get_mut(&id) {
Some(v) => *v = *v + *comp,
None => self.list_sum.insert(id, comp),
}
}
}
with the following errors.
error[E0614]: type `T` cannot be dereferenced
--> src/main.rs:20:34
|
20 | Some(v) => *v = *v + *comp,
| ^^^^^
error[E0369]: binary operation `+` cannot be applied to type `T`
--> src/main.rs:20:29
|
20 | Some(v) => *v = *v + *comp,
| ^^^^^^^^^^
|
= note: an implementation of `std::ops::Add` might be missing for `T`
I don't know what I have to change to get it to work. I use it with u32 type so it should have an + operator.
The Rust generics systems doesn't work the way C++ templates do: in C++ the compiler doesn't check whether the code actually compiles with any type in advance.
Rust makes sure the function compiles with any type that fulfills the listed requirements (called trait bounds). The compiler already told you what is missing: std::ops::Add might be missing for T, so ask for it:
impl<T: Add<Output = T>> simple_system<T> { /* … */ }
This will not fix everything; your code has other issues as well.
And here the Solution:
At first you should write a working non generic (c++ template) version and then evolve it to a generic version.
use std::collections::BTreeMap;
#[derive(Debug)]
struct SumUpSystem {
list_sum: BTreeMap<u64, i32 >,
list_com: BTreeMap<u64, Vec<i32> >
}
impl SumUpSystem {
pub fn new() -> SumUpSystem {
SumUpSystem {
list_sum: BTreeMap::new(),
list_com: BTreeMap::new()
}
}
pub fn add_component(&mut self, id: u64, comp: i32) {
self.list_com.entry(id).or_insert_with(Vec::new).push(comp);
let mut insert = false;
match self.list_sum.get_mut(&id) {
Some(x) => *x = *x + comp,
None => insert = true
}
if (insert) {
self.list_sum.insert(id, comp);
}
}
pub fn sum(& self, id: u64) -> i32 {
if let Some(x) = self.list_sum.get(&id) {
*x
} else {
panic!("Invalid id: Not in system!");
}
}
}
#[cfg(test)]
mod test {
use super::*;
macro_rules! init_test {
($v:ident) => (let mut $v : SumUpSystem = SumUpSystem::new(););
}
#[test]
fn add_component() {
init_test!(s);
s.add_component(1, 13);
assert_eq!(s.sum(1), 13);
s.add_component(1, 26);
assert_eq!(s.sum(1), 13 + 26);
}
}
The generic (c++ template). You should read the Trait section of the Rust Documentation to understand how/why it works.
use std::collections::BTreeMap;
use std::ops::Add;
#[derive(Debug)]
struct SumUpSystem<T> {
list_sum: BTreeMap<u64, T >,
list_com: BTreeMap<u64, Vec<T> >
}
impl <T: Add<Output=T> + Clone> SumUpSystem<T> {
pub fn new() -> SumUpSystem<T> {
SumUpSystem {
list_sum: BTreeMap::new(),
list_com: BTreeMap::new()
}
}
pub fn add_component(&mut self, id: u64, comp: &T) {
self.list_com.entry(id).or_insert_with(Vec::new).push(comp.clone());
let mut insert = false;
match self.list_sum.get_mut(&id) {
Some(x) => *x = x.clone() + comp.clone(),
None => insert = true
}
if insert {
self.list_sum.insert(id, comp.clone());
}
}
pub fn sum(& self, id: u64) -> T {
if let Some(x) = self.list_sum.get(&id) {
x.clone()
} else {
panic!("Invalid id: Not in system!");
}
}
}
#[cfg(test)]
mod test {
use super::*;
macro_rules! init_test {
($v:ident) => (let mut $v : SumUpSystem<i32> = SumUpSystem::new(););
}
#[test]
fn add_component() {
init_test!(s);
s.add_component(1, &13i32);
assert_eq!(s.sum(1), 13i32);
s.add_component(1, &26i32);
assert_eq!(s.sum(1), 39i32);
}
}

Move non-copyable struct across iterations

I am trying to access a variable inside a for loop. I can't implement Copy on the struct because it contains a String. How would I use the variable across iterations?
I get error E0382 when compiling. When I looked at the Rust documentation for the error, they mentioned using reference counting to solve the problem. Is this the only solution in my case?
#[derive(Clone)]
struct InputParser {
args: Vec<String>,
current: String,
consumed_quote: bool,
}
impl InputParser {
pub fn parse(input: String) -> Vec<String> {
let parser = InputParser {
args: Vec::new(),
current: String::new(),
consumed_quote: false,
};
for c in input.chars() {
match c {
'"' => parser.consume_quote(),
' ' => parser.consume_space(),
_ => parser.consume_char(c),
}
}
parser.end();
return parser.args;
}
pub fn consume_space(mut self) {
if !self.consumed_quote {
self.push_current();
}
}
pub fn consume_quote(mut self) {
self.consumed_quote = self.consumed_quote;
if self.consumed_quote {
self.push_current();
}
}
pub fn consume_char(mut self, c: char) {
self.current.push(c);
}
pub fn end(mut self) {
self.push_current();
}
pub fn push_current(mut self) {
if self.current.len() > 0 {
self.args.push(self.current);
self.current = String::new();
}
}
}
I want to access parser across iterations of the for loop.
[How do I] move [a] non-copyable struct across iterations
You don't, at least not trivially. Once you've moved the struct to a function, it's gone. The only way to get it back is for the function to give it back to you.
Instead, you most likely want to modify an existing struct inside the loop. You need to use a mutable reference for this:
use std::mem;
#[derive(Clone)]
struct InputParser {
args: Vec<String>,
current: String,
consumed_quote: bool,
}
impl InputParser {
fn consume_space(&mut self) {
if !self.consumed_quote {
self.push_current();
}
}
fn consume_quote(&mut self) {
self.consumed_quote = self.consumed_quote;
if self.consumed_quote {
self.push_current();
}
}
fn consume_char(&mut self, c: char) {
self.current.push(c);
}
fn end(&mut self) {
self.push_current();
}
fn push_current(&mut self) {
if self.current.len() > 0 {
let arg = mem::replace(&mut self.current, String::new());
self.args.push(arg);
}
}
}
fn parse(input: String) -> Vec<String> {
let mut parser = InputParser {
args: Vec::new(),
current: String::new(),
consumed_quote: false,
};
for c in input.chars() {
match c {
'"' => parser.consume_quote(),
' ' => parser.consume_space(),
_ => parser.consume_char(c),
}
}
parser.end();
parser.args
}
fn main() {}
Note that the previous way of taking the current argument would result in error[E0507]: cannot move out of borrowed content, so I switched to mem::replace. This prevents self.current from ever becoming an undefined value (which it was previously).
If you really want to pass everything by value, you need to return by value as well.
#[derive(Clone)]
struct InputParser {
args: Vec<String>,
current: String,
consumed_quote: bool,
}
impl InputParser {
fn consume_space(mut self) -> Self {
if !self.consumed_quote {
return self.push_current();
}
self
}
fn consume_quote(mut self) -> Self {
self.consumed_quote = self.consumed_quote;
if self.consumed_quote {
return self.push_current();
}
self
}
fn consume_char(mut self, c: char) -> Self {
self.current.push(c);
self
}
fn end(mut self) -> Self {
self.push_current()
}
fn push_current(mut self) -> Self {
if self.current.len() > 0 {
self.args.push(self.current);
self.current = String::new();
}
self
}
}
fn parse(input: String) -> Vec<String> {
let mut parser = InputParser {
args: Vec::new(),
current: String::new(),
consumed_quote: false,
};
for c in input.chars() {
parser = match c {
'"' => parser.consume_quote(),
' ' => parser.consume_space(),
_ => parser.consume_char(c),
}
}
parser = parser.end();
parser.args
}
fn main() {}
I believe this makes the API objectively worse in this case. However, you will see this style somewhat frequently with a builder. In that case, the methods tend to be chained together, so you never see a reassignment to the variable.

Resources