It's the first time I have used a parser combinator, so maybe I just have a misunderstanding about how I should use a parser combinator.
I have the following code which works in general:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=fd53125d3ed874b5e70f3aaae32e2e94
The problem is, I don't understand how I can get rid of the .unwrap() in my parse_method and parse_url functions and use the ? operator instead. I have already read https://github.com/rust-bakery/nom/blob/main/doc/error_management.md but absolutely don't understand how to apply it in this case.
I think I have to create a custom Error Enum and implement the ParseError trait. I tried that, but without any success. Unfortunately, the nom crate does not have many examples, and the ones that are there never use the ? operator.
This leads me to think that I misunderstood how I should use a parser combinator. Is the idea that I just parse the parts and assemble the parts later where I do not return IResult or is the idea that I should use .map_err and return noms own error types? I am really lost. Any help is appreciated.
Use map_res combinator:
fn parse_method(input: &[u8]) -> IResult<&[u8], Method> {
let (input, method) = map_res(
terminated(take_till(is_space), multispace0),
Method::from_bytes,
)(input)?;
Ok((input, method))
}
In general, you should prefer using combinators over manually successively calling parsers: full example
Related
I keep coming across the pattern in rust. My solution seems very verbose. Wondering if there a more idiomatic way of accomplishing the same thing.
I have function that about half way through might error. I need to either, get the result and keep processing or return an error. The error needs to a custom type and have some context.
async fn load_data() -> Result<SomeOutput, MyError> {
...
let raw = match str::from_utf8(&output.stdout) {
Ok(o) => o,
Err(e) => {
return Err(MyError::FailedParse(
"failed to read output from command X".into(),
e.to_string(),
));
}
};
...
}
I've looked into adding a From trait to the original error type, but then that error will always be converted into that type. I'd like more control about what error type is returned when.
Depends a bit on what exactly you need.
For starters, there's of course the ? operation. Not sure if operator is the right word. Of course that requires that the From trait for the underlying error is implemented for your error type, and it lacks the control you seek.
So then there's the various methods implemented on Result. You could use map_err to convert the underlying error to the error you actually want to return.
That can become quite a bit more cumbersome than you'd like, so there's also two crates to check out: anyhow and thiserror
What is the best way to call a function is when it returns a Result with a Unit Type in Rust?
For example I have this function to copy some text to clipboard:
use anyhow::Result;
use cli_clipboard::{ClipboardContext, ClipboardProvider};
fn copy_to_clipboard(text:&String) -> Result<()>{
let mut ctx = ClipboardContext::new()?;
ctx.set_contents(text.to_owned())?;
Ok(())
}
Then when I call this function I'd like to do this, but it creates a compiler warning "unused Result that must be used":
copy_to_clipboard(selected_value);
So I've ended up using the following instead which removes the warning:
_ = copy_to_clipboard(selected_value);
Just wondering if there is a better/more idiomatic way to do this?
Just wondering if there is a better/more idiomatic way to do this?
Well Depends.
Since the function returns a Result, it's signaling that it can fail. The () means it has no useful return value if it succeeds (this is common for functions with only side-effects).
So the question is really what you want to do if (when?) the function fails. What you've said here is "I don't care and I want to ignore it", which is perfectly valid (though you may want to document the reasoning via a comment).
Other possibilities are handling the error (e.g. recovering, displaying an error message, ...), bubbling the error up to your own caller, panic-ing (using unwrap or expect), ...
I'm writing code to parse HTTP requests. I'd rather keep some things like request methods and response codes as enums because of their exhaustibility.
But it also means that for each of those enums I would have to make a From<String> implementation. The obvious way to do it is to match against a plethora of constant strings. That seems like a lot of almost identical code which I would want to factor out.
Someone suggested I use a macro to do it, but my knowledge of rust macros is limited so it made me question some things:
How can I ensure the argument is of type &str or String? Macros can take expressions, but I don't want to use type_name and panic if it doesn't match as it seems to be a very dirty fix.
How can I convert a string to a token stream? So far I've seen that the proc_macro crate offers that functionality, but is it possible to do with simple declarative macro?
Is it possible to achieve one to one mapping by any other means? I hoped I could pre-initialize enum variants with some known values like in Java.
You really want to use a library for this. You can write the macro yourself, but someone else has already made a battle-tested version of what you're trying to do.
Use strum's EnumString derive:
In Cargo.toml:
[dependencies]
strum = "0.24"
strum_macros = "0.24"
In your source code: (eg main.rs)
use std::str::FromStr;
use strum_macros::EnumString;
#[derive(Debug, EnumString)]
enum MyEnum {
Foo,
Bar,
Baz,
}
fn main() {
dbg!(MyEnum::from_str("Foo").unwrap());
}
Gives you
[src/main.rs:12] MyEnum::from_str("Foo").unwrap() = Foo
For more details see The documentation for the FromString derive and strum's readme.
Okay so I'm using a library which is ffi and uses C, for whatever reason when I'm getting an iterator of values from one of the libraries functions the function never fails but returns some wrong stuff if it does fail. I've already dealt with all the checks to make sure what the user (of this api) is iterating over is correct information or no/less information (if some of its wrong) but i need to wrap all the values of the iterator to a Result even though this library isn't returning options or results so that it works with a bunch of other code (this is a generic trait I'm implementing).
I tried taking my iterator and using .map(|res| Ok(res)) however this does not work because the compiler cannot infer the type for e.
I have also tried using Result<_,_> like .map(|res| Result<res, _>) but apparently this is not an expression
Slight modification to the last one i also tried .map(|res| new: Result<res, _>) however then the result is non mutable while res is and it once again fails.
So I'm not really sure what to do here. I would really prefer the mapping of all the values to be inline and the error can be anything because there will never be a meaningful error however i still need a Result object for my generic trait.
P.S. im not sure if it will help but the type of res is (alloc::boxed::Box<[u8]>, alloc::boxed::Box<[u8]>).
As the error type, I'd use std::convert::Infallible, because that's what it's for: "The error type for errors that can never happen.".
For specifying the error type, the turbofish syntax (see bottom of this page, e.g.) is probably the most appropriate. There are several ways you could use it:
.map(|res| Ok::<_, Infallible>(res))
.map(|res| Result::<_, Infallible>::Ok(res))
or, as the eta-reduced variant .map(Ok::<_, Infallible>)
Alternatively, you can
use the syntax for specifying the return type of your closure:
.map(|res| -> Result<_, Infallible> { Ok(res) }).
use as: .map(|res| Ok(res) as Result<_, Infallible>)
Note that the diffference between these is that Result::<_, Infallible>::Ok is a value (a constructor function, but functions are values), but Result<_, Infallible> is a type. Further note that the _ is a placeholder for a type, not some magic for a value/parameter (The syntax in your question looks awfully Scala-y, so I wanted to make clear that these are completely different.) You can replace the _ by (Box<[u8]>, Box<[u8]>), if you want.
Playground
I'm trying to understand the need for the Trait:: and <T as Trait>:: method invocation syntax. In particular, I'm looking at the following function from this answer:
fn clone_into_array<A, T>(slice: &[T]) -> A
where
A: Default + AsMut<[T]>,
T: Clone,
{
assert_eq!(
slice.len(),
std::mem::size_of::<A>() / std::mem::size_of::<T>()
);
let mut a = Default::default();
<A as AsMut<[T]>>::as_mut(&mut a).clone_from_slice(slice);
a
}
It seems that the middle two method invocation lines can be rewritten as:
let mut a = A::default();
a.as_mut().clone_from_slice(slice);
I believe this is more readable, as we already know that A implements Default and AsMut<[T]> and we can invoke as_mut directly as a method instead of having to pass a to it explicitly.
However, am I missing a good reason for the linked answer to have written it more verbosely? Is it considered good style? Are the two semantically different under certain conditions?
I agree with your rewrites — they are more clear and are what I would recommend for that function.
am I missing a good reason for the linked answer to have written it more verbosely?
My guess is that the author simply was tired of writing that function and stopped. If they took a look at it again, they might refine it to be shorter.
Is it considered good style?
I don't think there's a general community stylistic preference around this yet, other than the general "shorter is better until it isn't".
Are the two semantically different under certain conditions?
They shouldn't be.
There are times where the <>:: syntax is needed because otherwise it would be ambiguous. One example from a recent question:
let array = <&mut [u8; 3]>::try_from(slice);
Another time is when you don't have a nicely-named intermediate type or the intermediate type is ambiguous across multiple traits. One gross example is from a where clause but shows the same issue as an expression would:
<<Tbl as OrderDsl<Desc<Expr>>>::Output as LimitDsl>::Output: QueryId,
See also:
How to call a method when a trait and struct use the same name?