Destructure rust enum without matching - rust

What would be the correct way to simplify this strange code? There is no variant of AstNode that doesn't wrap around an inner structure.
impl<'a> Debug for AstNode<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AstNode::Literal(inner) => inner.fmt(f),
AstNode::Variable(inner) => inner.fmt(f),
AstNode::Binary(inner) => inner.fmt(f),
AstNode::Unary(inner) => inner.fmt(f),
AstNode::Call(inner) => inner.fmt(f),
AstNode::Function(inner) => inner.fmt(f),
AstNode::If(inner) => inner.fmt(f),
AstNode::While(inner) => inner.fmt(f),
AstNode::Begin(inner) => inner.fmt(f),
AstNode::Assign(inner) => inner.fmt(f),
AstNode::NewGlobal(inner) => inner.fmt(f),
AstNode::Error(inner) => inner.fmt(f),
AstNode::TestAssert(inner) => inner.fmt(f),
AstNode::TestExpect(inner) => inner.fmt(f),
}
}
}

If the inner member is not dependent on enum variant there is no need to put it in enum. You can promote it up, so there will be no need to match it. Something along those lines:
struct AstNode(AstKind, InnerType);
enum AstKind {
Literal,
Variable,
...
}
then you can write:
impl<'a> Debug for AstNode<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.1.fmt(f)
}
}
This type system explicitly states that InnerType and AstKind is independent. Which is your case If I understand correctly.

Related

How to infer other library error in result type

Im new to rust
consider this following code
fn my_function(text: &str) -> Result<String, _WHAT_SHOULD_I_USE_HERE_> {
let word = OtherLib::text(&text)?;
...
Ok(word)
}
OtherLib::text has a return type of Result<String, anyhow::Error>
but my problem is rust force me to add return type on my_function
I am not using anyhow on my project
is there any way to solve this, aside/without installing anyhow. And is there any easy way to auto complete this on vscode
what i expected
fn my_function(text: &str) -> Result<String, _JUST_INFER_IT_> {
and the caller of my function will get
Result<String, Error1 | Error2>
btw using _ is not working for me
fn my_function(text: &str) -> Result<String, _> {
// the placeholder `_` is not allowed within types on item signatures for return types
Part 2
I think rust don't have a union😅
so base on following the OtherLib::text, I come up with the following imaginary type
imaginary type: Result<String, ErrorKindEnum1 | ErrorKindEnum2 | Error>
current type : Result<String, Error>
where | is union type and Error at the end is just a subset/value/kind of ErrorKindEnum3, example ErrorKindEnum3::Error1
my new problem is how I can match it like, because vscode dosent give me any intellisense
match value {
ErrorKindEnum1::Error1 => todo!(),
ErrorKindEnum1::Error2 => todo!(),
ErrorKindEnum2::Error1 => todo!(),
...
ErrorKindEnum3::Error1 => todo!(), // this is okay
ErrorKindEnum3::Error2 => todo!(), // this should error. Not possible
}
as much as possible without type casting, or hardcoding type😅
Part 3
enum MyError {
A, B, C
}
fn greet () -> MyError {
if ... {
return MyError::A;
}
if ... {
return MyError::B;
}
}
fn main() {
match greet() {
MyError:A => todo!(),
MyError:B => todo!(),
MyError:C => todo!(), // rust cant tell me this is impossible?
}
}

Handling a Response from a Result using match

I'm trying to return a Result from a function and extract it's return.
I'm using i32 and a &str and I get a mismatch type error in the match statment (Can't use two different types in a match).
How do I fix this?
fn use_result(par: i32)-> Result<i32, &'static str> {
if par == 0 {
Err("some error")
} else {
println!("par is 1");
Ok(par)
}
}
fn main() {
// Result
let res = match use_result(1) {
Ok(v) => v,
Err(e) => e,
};
}
//Do something with res: v or res: e
}
In Rust, every variable has a single type. In the code you have now, res is either a &'static str or an i32, which is not allowed.
Your options are:
Return early
fn main() {
let res: i32 = match use_result(1) {
Ok(v) => v,
Err(e) => return,
};
}
Different code in each match arm
fn main() {
match use_result(1) {
Ok(v) => {
handle_success(v);
},
Err(e) => {
handle_error(e);
},
};
}
Return an enum
Enums allow you to express that a type is "one of these possible variants" in a type safe way:
enum IntOrString {
Int(i32),
String(&'static str),
}
fn main() {
let i_or_s: IntOrString = match use_result(1) {
Ok(v) => IntOrString::Int(v),
Err(e) => IntOrString::String(e),
};
}
But this is a bit weird, since Result<i32, &'static str> is already an enum, if you want to do anything with an IntOrString you'll need to match on it later on (or an if let, etc).
Panic
fn main() {
let res: i32 = match use_result(1) {
Ok(v) => v,
Err(e) => panic!("cannot be zero"),
};
}
This is more cleanly expressed as use_result(1).unwrap(). It's usually not what you want, since it doesn't allow the caller of the function to recover/handle the error. But if you're calling this from main(), the error has nowhere else to propagate to, so unwrapping is usually OK.

How do I return a custom error from a splitn fn?

I've written this function to parse a comma-separated string and return either a <Vec<&str>> or a custom error:
fn parse_input(s: &str) -> Result<Vec<&str>, ParseError> {
match s.splitn(2, ',').next() {
Ok(s) => s.collect::<Vec<&str>>(),
Err(err) => Err(ParseError::InvalidInput)
}
}
The compiler gives me this response:
Ok(s) => s.collect::<Vec<&str>>(),
^^^^^ expected enum `Option`, found enum `Result`
...
Err(err) => Err(ParseError::InvalidInput)
^^^^^^^^ expected enum `Option`, found enum `Result`
My problem is that I don't understand how to change the code to satisfy the compiler. What is wrong with this function?
.next() returns an Option<&str>, i.e. Some(s) or None.
fn parse_input(s: &str) -> Result<Vec<&str>, ParseError> {
match s.splitn(2, ',').next() {
Some(s) => s.collect::<Vec<&str>>(),
None => Err(ParseError::InvalidInput),
}
}
Just like you wrapped the error with Err to make it a Result, the non-error needs to be wrapped with Ok.
fn parse_input(s: &str) -> Result<Vec<&str>, ParseError> {
match s.splitn(2, ',').next() {
Some(s) => Ok(s.collect::<Vec<&str>>()),
None => Err(ParseError::InvalidInput),
}
}
Whether it’s the pattern-matched Some(s) or the outer parameter s, s.collect() doesn’t make sense. Going by your description, maybe you want to split the string on commas, collect that into a Vec, and produce an error if the result doesn’t consist of exactly two parts?
fn parse_input(s: &str) -> Result<Vec<&str>, ParseError> {
let parts: Vec<_> = s.split(',').collect();
if parts.len() == 2 {
Ok(parts)
} else {
Err(ParseError::InvalidInput)
}
}
Maybe a pair would be better? Also, if more than one comma is acceptable and you just want to split on the first one, split_once fits perfectly.
fn parse_input(s: &str) -> Result<(&str, &str), ParseError> {
s.split_once(',').ok_or(ParseError::InvalidInput)
}

How to use `Option<MyStruct>` on format?

I'm trying to use an Option of a Method on format!
/// Converts a method into a `&str`.
impl<'a> From<&'a Method> for &'a str {
fn from(v: &'a Method) -> Self {
match v {
Method::Describe => "DESCRIBE",
Method::GetParameter => "GET_PARAMETER",
Method::Options => "OPTIONS",
Method::Pause => "PAUSE",
Method::Play => "PLAY",
Method::PlayNotify => "PLAY_NOTIFY",
Method::Redirect => "REDIRECT",
Method::Setup => "SETUP",
Method::SetParameter => "SET_PARAMETER",
Method::Teardown => "TEARDOWN",
Method::Extension(ref v) => v,
}
}
}
How do I use a Option<Method> in format!? I don't want to use unwrap, I want it to be empty in case there's no Method
I guess it's something like this:
let method = `Some(Method::Describe);
let method = match method {
Some(method) => method.something,
None => ""
};
I believe in this case you can use map_or_else
So what you have would looks something like this?
let method = Some(Method::Describe);
let method = method.map_or_else(|| String::default(), |m| m.something);

Update / re-initialize a var defined in lazy_static

I have a config that I want initialized on startup, but not have to re-read the file every time. To do this, I'm using lazy_static:
lazy_static! {
static ref SETTINGS: Settings = {
match Settings::init() {
Ok(c) => c,
Err(e) => panic!("{}", e),
}
};
}
But now I have a method that updates that config file, and I want to "re-initialize" / update it, without having to re-start the program.
pub fn save_config_file(data: &str) -> Result<String, Error> {
fs::write(CONFIG_FILE, data)?;
SETTINGS = {
match Settings::init() {
Ok(c) => c, // The line with the error
Err(e) => panic!("{}", e),
}
};
Self::read_config_file()
}
Which gives me the error: [rustc E0308] [E] mismatched types expected struct settings::SETTINGS, found struct settings::Settings
Is there any way to re-initialize a lazy_static? Or do I have to restart the program?
Okay, figured this one out. Mainly from here: How do I assign a String to a mutable static variable?
Add derive Clone to your struct:
#[derive(Debug, Deserialize, Clone)]
Wrap your lazy_static in an RwLock, initialize it normally.
use std::sync::RwLock;
lazy_static! {
static ref SETTINGS: RwLock<Settings> = RwLock::new(
match Settings::init() {
Ok(c) => c,
Err(e) => panic!("{}", e),
}
);
}
To read, you can do this:
pub fn get() -> Self {
SETTINGS.read().unwrap().to_owned()
}
To write:
let mut new_settings = SETTINGS.write().unwrap();
*new_settings = match Settings::init() {
Ok(c) => c,
Err(e) => panic!("{}", e),
};

Resources