I have question about make action after panic!. I have strong experience with python, and often I use:
try:
do_something()
exception:
do_something_else()
But can't do that in Rust. My code is bellow:
fn do_something(value: u32) -> Result<u32, io::Error> {
if value > 30 {
panic!("Value is bigger then 30!");
}
else {
Ok(value)
}
}
// first try
fn if_else_catch(value: u32) -> Result<u32, u32> {
let result = do_something(value);
let something = match result {
Ok(something) => something + something,
Err(err) => do_something_else(),
};
Ok(something)
}
fn if_else_catch_retun_different_value(value: u32) -> Result<u32, u32> {
let result = do_something(value);
let something = match result {
Ok(something) => something + something,
Err(err) => 100,
};
Ok(something)
}
// another try
fn do_something_else_with_questionmark(value: u32) -> Result<u32, io::Error> {
let result = do_something(value)?;
if let Err(_err) = do_something(value) {
println!("An Error occured!");
}
Ok(result)
}
fn main() {
println!("Do something: {}", do_something(35).unwrap());
println!("Do something res: {}", if_else_catch(35).unwrap());
}
Is it possible to do this?
How can I implement it?
I already check documentation, but example like this:
let greeting_file_result = File::open("hello.txt");
let greeting_file = match greeting_file_result {
Ok(file) => file,
Err(error) => panic!("Problem opening the file: {:?}", error),
};
^
|
Doesn't work for this case.
The natural thing would be to just return an Err in your error case:
use std::io::{Error, ErrorKind};
fn do_something(value: u32) -> Result<u32, Error> {
if value > 30 {
Err(Error::new(ErrorKind::Other, "Value is bigger than 30!"))
} else {
Ok(value)
}
}
If you really have to you can use catch_unwind to recover from panics but
It is not recommended to use this function for a general try/catch mechanism. The Result type is more appropriate to use for functions that can fail on a regular basis. Additionally, this function is not guaranteed to catch all panics.
use std::panic::catch_unwind;
fn catch_it(value: u32) -> Result<u32, u32> {
let result = catch_unwind(|| do_something(value));
let something = match result {
Ok(Ok(something)) => something + something,
Ok(Err(_)) | Err(_) => do_something_else(),
};
Ok(something)
}
Related
I have following struct and want to test implementation of Display trait:
use core::fmt::{Display, Formatter, Result};
struct B {}
impl Display for B {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
write!(f, "A {} C", "B")
}
}
#[test]
fn it_works() {
assert_eq!(format!("{}", B {}), "A B C")
}
it works in general case, however in #![no_std] environment it yields an error due to the fact that format! macro is missing (it allocates, so cannot be used in no_std).
Is there idiomatic way to test core::std::Display trait in no_std scenarios?
I've tried to create my own core::std::Formatter instance with custom buffer implementation to somehow circumvent it, and test fmt method directly, but instancing this type is considered compiler internal.
The best option is to enable std for tests:
#![cfg_attr(no_std, not(test))]
If you cannot do it, and you know an upper limit for the format size, you can implement core::fmt::Write for a u8 array wrapper and using it. The arrayvec has already got you covered with ArrayString:
let mut formatted = ArrayString::<10>::new();
use core::fmt::Write;
write!(formatted, "{}", B {}).expect("failed to format, probably buffer too small");
assert_eq!(&formatted, "A B C")
Playground.
Why not just implement core::fmt::Write on your custom buffer and use core::fmt::write! to it?
format! is basically a write! to a string buffer, both really call Write::write_fmt, with format! having the conveniences that it provides its own (string) buffer and panics on error.
macro_rules! write {
($dst:expr, $($arg:tt)*) => {
$dst.write_fmt($crate::format_args!($($arg)*))
};
}
macro_rules! format {
($($arg:tt)*) => {{
let res = $crate::fmt::format($crate::__export::format_args!($($arg)*));
res
}}
}
pub fn format(args: Arguments<'_>) -> string::String {
let capacity = args.estimated_capacity();
let mut output = string::String::with_capacity(capacity);
output.write_fmt(args).expect("a formatting trait implementation returned an error");
output
}
It's possible to do it without allocation, and without any buffer, just by implementing core::fmt::Write trait, as #Masklinn mentioned.
Here is sample implementation:
#![no_std]
use core::fmt::{Display, Formatter, Result, Write};
struct B {}
impl Display for B {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
write!(f, "A {} C", "B")
}
}
struct Comparator<'a> {
valid: bool,
to_compare: &'a str
}
impl<'a> Comparator<'a> {
fn new(s: &'a str) -> Self {
Self { valid: true, to_compare: s }
}
fn is_valid(self) -> bool {
self.valid && self.to_compare.is_empty()
}
}
impl<'a> Write for Comparator<'a> {
fn write_str(&mut self, s: &str) -> Result {
if s.eq(self.to_compare) {
self.valid = self.valid && true;
self.to_compare = "";
return Ok(());
}
if self.to_compare.starts_with(s) && self.to_compare.len() >= s.len() {
self.to_compare = &self.to_compare[s.len()..];
} else {
self.valid = false
}
Ok(())
}
}
#[test]
fn it_works() {
let mut cmp = Comparator::new("A B C");
let _ = write!(&mut cmp, "{}", B{});
assert!(cmp.is_valid());
}
#[test]
fn too_short() {
let mut cmp = Comparator::new("A B");
let _ = write!(&mut cmp, "{}", B{});
assert!(!cmp.is_valid());
}
#[test]
fn too_long() {
let mut cmp = Comparator::new("A B C D");
let _ = write!(&mut cmp, "{}", B{});
assert!(!cmp.is_valid());
}
EDIT: fixed bug in the code, was:
if self.to_compare.starts_with(s) && self.to_compare.len() >= s.len() {
self.to_compare = &self.to_compare[s.len()..];
self.valid = true
} else {
self.valid = false
}
fixed:
if self.to_compare.starts_with(s) && self.to_compare.len() >= s.len() {
self.to_compare = &self.to_compare[s.len()..];
} else {
self.valid = false
}
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.
#![allow(non_camel_case_types)]
use libc::{c_uchar, size_t};
use std::str::FromStr;
use std::ffi::{CString, NulError};
use std::slice;
#[repr(C)]
pub struct c_str_t {
pub len: size_t,
pub data: *const c_uchar,
}
pub trait MyCStrExt<T> {
fn to_c_str(&self) -> Result<c_str_t, NulError>;
}
pub trait MyCStringExt {
fn from_c_str_ref(nstr: &c_str_t) -> Option<String>;
}
impl<'a> MyCStrExt<&'a str> for str {
fn to_c_str(&self) -> Result<c_str_t, NulError> {
let result = match CString::new(&self[..]) {
Ok(result) => result,
Err(e) => {
return Err(e);
}
};
Ok(c_str_t { data: result.as_ptr() as *const u8, len: self.len() })
}
}
impl MyCStringExt for String {
fn from_c_str_ref(nstr: &c_str_t) -> Option<String> {
unsafe {
if nstr.data.is_null() {
return None;
}
let value = slice::from_raw_parts(nstr.data, nstr.len);
match String::from_utf8(value.to_vec()) {
Ok(value) => Some(value),
Err(e) => None
}
}
}
}
With this test that first converts to a CString and then back again to a Rust string, passing in the given string
#[test]
fn test_to_c_str() {
let s = "What does the fox say?";
let result = s.to_c_str();
let round_trip = String::from_c_str_ref(result.as_ref().ok().unwrap());
println!("{:?}", round_trip);
}
will result in a round trip with a Rust string at the end with a null in the first character position:
Some("\u{0}hat does the fox say?")
What am I doing wrong?
You've missed one crucial step.
fn to_c_str(&self) -> Result<c_str_t, NulError> {
let result = match CString::new(&self[..]) {
Ok(result) => result,
Err(e) => {
return Err(e);
}
};
Ok(c_str_t { data: result.as_ptr() as *const u8, len: self.len() })
}
allocates a new CString structure, and takes a pointer to its data, but that data will still be freed once the to_c_str function runs to completion. This means that later code can overwrite the string contents in-memory. In your example case, it just happens to be that only the first character is overwritten.
I'd recommend reading over the documentation of .as_ptr() as it tries to cover some of this.
You could manually std::mem::forget, e.g.
fn to_c_str(&self) -> Result<c_str_t, NulError> {
let result = match CString::new(&self[..]) {
Ok(result) => result,
Err(e) => {
return Err(e);
}
};
let s = c_str_t { data: result.as_ptr() as *const u8, len: self.len() };
std::mem::forget(result);
Ok(s)
}
but the best approach would be to use .into_raw() to take ownership and return the pointer on its own.
fn to_c_str(&self) -> Result<c_str_t, NulError> {
let result = match CString::new(&self[..]) {
Ok(result) => result,
Err(e) => {
return Err(e);
}
};
Ok(c_str_t { data: result.into_raw() as *const u8, len: self.len() })
}
I have some code that looks like this:
trait Stack {
fn top(&mut self) -> Option<f64>;
}
impl Stack for Vec<f64> {
fn top(&mut self) -> Option<f64> {
match self.pop() {
None => None,
Some(v) => {
self.push(v);
Some(v)
}
}
}
}
fn main() {
let mut stack: Vec<f64> = Vec::new();
stack.push(5.3);
stack.push(2.3);
stack.push(1.3);
match stack.top() {
Some(v) => println!("Top of the stack: {}", v),
None => println!("The stack is empty"),
}
}
Right now, the top() method is modifying self, but I think that this should not be necessary. The obvious way to do it didn't really work:
fn top(&mut self) -> Option<f64> {
match self.len() {
0 => None,
n => self[n - 1],
}
}
I've toyed around a bit with converting usize to i32 and back, but none of what I'm writing looks as short and readable as I think it should.
You can use slice::last:
fn top(&mut self) -> Option<f64> {
self.last().copied()
}
Option::copied (and Option::cloned) can be used to convert from an Option<&f64> to an Option<f64>, matching the desired function signature.
You can also remove the mut from both the implementation and the trait definition.
And just after posting the question, the answer appears to be obvious:
fn top (&mut self) -> Option<&f64> {
match self.len() {
0 => None,
n => Some(&self[n-1])
}
}
I.e. the usize was never the problem - the return type of top() was.
I have a crate with the function sys_info::hostname which returns the hostname. The only problem is that hostname returns Result<String, Error>, but I need another function that must have a return type Result<(), String>. How can I call sys_info::hostname and return the hostname in a function that doesn't return the same type? Before you ask, the second function's return type cannot be changed due to formatting issues.
Instead of using the try! macro, which you can't use if the result type is incompatible, use a match statement to take apart the return value of sys_info::hostname and do what you need to with its parts. Example
struct Error;
fn thing_returning_result(succeed: bool) -> Result<String, Error> {
if succeed {
Ok("Hello".into())
} else {
Err(Error)
}
}
fn thing_returning_other_result(succeed: bool) -> Result<(), String> {
match thing_returning_result(succeed) {
Ok(s) => Err(s),
Err(_) => Err("whoopsies".into())
}
}
fn main() {
println!("{:?}", thing_returning_other_result(false));
}
I'd use map and map_err:
struct Error;
fn inner() -> Result<String, Error> {
Err(Error)
}
fn outer() -> Result<(), String> {
inner()
.map(|ok_val| ())
.map_err(|err_val| "Something".to_string())
}
fn main() {
outer();
}
Here's an example:
First try:
fn is_url(val: &str) -> Result<(), String> {
match Url::parse(val) {
Ok(_) => Ok(()),
Err(_) => Err(format!("The URL '{}' is an invalid URL.", val)),
}
}
More concise:
fn is_url(val: &str) -> Result<(), String> {
Url::parse(val).map_err(|_| format!("The URL '{}' is an invalid URL.", val))?;
Ok(())
}