I'm writing some Rust code which uses the ? operator. Here is a few lines of that code:
fn files() -> Result<Vec<std::string::String>, Box<Error>> {
let mut file_paths: Vec<std::string::String> = Vec::new();
...
file_paths.push(pathbuf.path().into_os_string().into_string()?);
...
Ok(file_paths)
}
However, even though I'm using ? on a Result it is giving me the following error:
`the trait `StdError` is not implemented for `OsString`.
This is contrary to the Rust documentation here, which states that:
The ? is shorthand for the entire match statements we wrote earlier. In other words, ? applies to a Result value, and if it was an Ok, it unwraps it and gives the inner value. If it was an Err, it returns from the function you're currently in.
I've confirmed that pathbuf.path().into_os_string().into_string() is of type Result, because when I remove the ?, I get the following compiler error:
expected struct `std::string::String`, found enum `std::result::Result`
(since file_paths is a Vector of strings, not Results).
Is this a bug with the Rust language or documentation?
In fact I tried this without pushing to the Vector, but simply initializing a variable with the value of pathbuf.path().into_os_string().into_string()?, and I got the same error.
The function OsString::into_string is a little unusual. It returns a Result<String, OsString> - so the Err variant is actually not an error.
In the event that the OsString cannot be converted into a regular string, then the Err variant is returned, containing the original string.
Unfortunately this means you cannot use the ? operator directly. However, you can use map_err to map the error variant into an actual error, like this:
file_paths.push(
pathbuf.path()
.into_os_string()
.into_string().
.map_err(|e| InvalidPathError::new(e))?
);
In the above example, InvalidPathError might be your own error type. You could also use an error type from the std library.
Related
I've seen this pattern on more than one occasion:
fn f() -> Result<..., ...> {
...
Ok(expression()?)
}
Specifically, I find the order Ok(expression()?) confusing. What's the return type of expression()? and is there an interplay between Ok and this type? It seems, the function Ok must do more than just capturing the value. But how can it force a return with an error, if it's the last expression wrapping the return type of expression()?.
The ? operator either returns (as in the return statement) the error variant, or extracts the value from the Ok variant. Thus Ok(expression()?) is more or less equal to:
// This pattern is so common in rust, that the language designers
// introduced the `?` to reduce the amount of boilerplate needed
let result = expression();
let r = match result{
Err(e) => return Err(e.into()), // tries to convert the error into the required type if necessary (and if possible)
Ok(r) => r
}
Ok(r)
So basically Ok(expression()?) can be simplified to just
expression() if its Err variant matches the one from the function definition.
Historical context:
Extracting the Ok() variant and propagating the Err variant is a very common pattern in rust. In order to reduce the amount of boilerplate code, the rust team introduced the try!() macro; But it's so clumsy and does not really work well with method chaining. The rust team introduced the ? operator, which is doing absolutely the same thing, except for it's not so intrusive as try!()
? just leverages the error returned by expression if it exists or unwraps the value from within the returned Ok if not. Ok(expression()?) could be just expresion() and clippy would warn you about that:
warning: question mark operator is useless here
--> src/lib.rs:8:5
|
8 | Ok(expression()?)
| ^^^^^^^^^^^^^^^^^ help: try: `expression()`
|
= note: `#[warn(clippy::needless_question_mark)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark
Playground
You can follow the documentation for further information
As per the comments. Notice that ? is useful if the error types differ but you have an implementation of From between error types or you use Box<dyn Error> as return type.
In which case you can either, bubble up the error if it is an error, otherwise rewrap the value with an Ok
Ok(expression()?)
or, map your error type into the correct error type with map_err
expression.map_err(|e| ...)
Context: I am learning Rust & WebAssembly and as a practice exercise I have a project that paints stuff in HTML Canvas from Rust code. I want to get the query string from the web request and from there the code can decide which drawing function to call.
I wrote this function to just return the query string with the leading ? removed:
fn decode_request(window: web_sys::Window) -> std::string::String {
let document = window.document().expect("no global window exist");
let location = document.location().expect("no location exists");
let raw_search = location.search().expect("no search exists");
let search_str = raw_search.trim_start_matches("?");
format!("{}", search_str)
}
It does work, but it seems amazingly verbose given how much simpler it would be in some of the other languages I have used.
Is there an easier way to do this? Or is the verbosity just the price you pay for safety in Rust and I should just get used to it?
Edit per answer from #IInspectable:
I tried the chaining approach and I get an error of:
temporary value dropped while borrowed
creates a temporary which is freed while still in use
note: consider using a `let` binding to create a longer lived value rustc(E0716)
It would be nice to understand that better; I am still getting the niceties of ownership through my head. Is now:
fn decode_request(window: Window) -> std::string::String {
let location = window.location();
let search_str = location.search().expect("no search exists");
let search_str = search_str.trim_start_matches('?');
search_str.to_owned()
}
which is certainly an improvement.
This question is really about API design rather than its effects on the implementation. The implementation turned out to be fairly verbose mostly due to the contract chosen: Either produce a value, or die. There's nothing inherently wrong with this contract. A client calling into this function will never observe invalid data, so this is perfectly safe.
This may not be the best option for library code, though. Library code usually lacks context, and cannot make a good call on whether any given error condition is fatal or not. That's a question client code is in a far better position to answer.
Before moving on to explore alternatives, let's rewrite the original code in a more compact fashion, by chaining the calls together, without explicitly assigning each result to a variable:
fn decode_request(window: web_sys::Window) -> std::string::String {
window
.location()
.search().expect("no search exists")
.trim_start_matches('?')
.to_owned()
}
I'm not familiar with the web_sys crate, so there is a bit of guesswork involved. Namely, the assumption, that window.location() returns the same value as the document()'s location(). Apart from chaining calls, the code presented employs two more changes:
trim_start_matches() is passed a character literal in place of a string literal. This produces optimal code without relying on the compiler's optimizer to figure out, that a string of length 1 is attempting to search for a single character.
The return value is constructed by calling to_owned(). The format! macro adds overhead, and eventually calls to_string(). While that would exhibit the same behavior in this case, using the semantically more accurate to_owned() function helps you catch errors at compile time (e.g. if you accidentally returned 42.to_string()).
Alternatives
A more natural way to implement this function is to have it return either a value representing the query string, or no value at all. Rust provides the Option type to conveniently model this:
fn decode_request(window: web_sys::Window) -> Option<String> {
match window
.location()
.search() {
Ok(s) => Some(s.trim_start_matches('?').to_owned()),
_ => None,
}
}
This allows a client of the function to make decisions, depending on whether the function returns Some(s) or None. This maps all error conditions into a None value.
If it is desirable to convey the reason for failure back to the caller, the decode_request function can choose to return a Result value instead, e.g. Result<String, wasm_bindgen::JsValue>. In doing so, an implementation can take advantage of the ? operator, to propagate errors to the caller in a compact way:
fn decode_request(window: web_sys::Window) -> Result<String, wasm_bindgen::JsValue> {
Ok(window
.location()
.search()?
.trim_start_matches('?')
.to_owned())
}
I have some code that's supposed to get image filenames from a database and add them to a vector.
extern crate postgres;
use postgres::{Connection, TlsMode};
fn main() {
let conn = Connection::connect(
"postgres://postgres:password#localhost:5432/test",
TlsMode::None,
).unwrap();
let mut filenames = Vec::new();
if let Ok(filename_results) = conn.query("SELECT filename FROM images", &[]) {
for row in &filename_results {
filenames.push(format!("{}.jpg", row.get(0)));
}
}
println!("{:?}", filenames);
}
This fails with:
error[E0283]: type annotations required: cannot resolve `_: postgres::types::FromSql`
--> src/main.rs:14:54
|
14 | filenames.push(format!("{}.jpg", row.get(0)));
| ^^^
I don't understand why Rust can't figure out the type in this context, though I've figured out a way to make it work. I'm wondering what the simplest/idiomatic way to tell format!() what types it should be expecting are, and why row.get(0) doesn't need a type annotation unless I slap a format!() around it. This is my best attempt at a solution:
for row in &filename_results {
let filename: String = row.get(0);
filenames.push(format!("{}.jpg", filename));
}
Let's look at the signature of the function you're calling:
fn get<I, T>(&self, idx: I) -> T
where
I: RowIndex + Debug,
T: FromSql,
That is, this function actually has two type parameters, I and T. It uses I as the type to index with. The argument you pass has this type. T is the return type. The constraints (the where clause) don't really matter here, but they specify that the argument type I has to be something postgres can use as a row index, and the return type T has to be something postgres can create from an SQL result.
Usually, Rust can infer the type parameters of functions. Argument types are usually easier to infer, because there's a value of the desired type right there. Even C++ can infer argument types! Return types are harder to infer because they depend on the context the function is called from, but Rust can often infer those too.
Let's look at your function call and the context it's used:
format!("{}.jpg", row.get(0))
Here's it's obvious that the argument is an integer, because it's a literal, and it's right there. There are rules for working out what integer types it could be, but in this case, it has to be usize because that's the only one the RowIndex trait is implemented for.
But what return type are you expecting? format! can take almost any type, so the compiler has no way to know what get needs to return. All it knows is that T has to have the FromSql trait. This is what the error message tells you:
error[E0283]: type annotations required: cannot resolve `_: postgres::types::FromSql`
Luckily, Rust has a syntax for explicitly passing function parameters to functions, so you don't have to rely on its type inference. Shepmaster wrote a good explanation of it in this answer to a similar question. Jumping straight to the answer, you can write row.get::<_, String>(0) to only specify the second type parameter, and let inference work on the first type parameter.
You specifically ask for a more idiomatic way to specify the type, and I think what you already have is more idiomatic. With the explicit type parameter, a reader still needs to understand the signature of get to know that String will be the return type. It's not always the case that the second type parameter will be the return type, and it's easy to get confused and specify them in the wrong order. By naming and type-annotating the result, you make it obvious what value the type annotation refers to.
let filename: String = row.get(0);
filenames.push(format!("{}.jpg", filename));
If you do want to write your code in the more functional style that Shepmaster suggested, you can still use this style:
let filenames = filename_results.map(|row| { let f: String = row.get(0); format!("{}.jpg", f) }).collect();
and break the "one-liner" across lines if that suits your taste.
I have a piece of code like this:
use std::cell::RefCell;
use std::rc::Rc;
struct A(bool);
impl A {
fn get_ref(&self) -> &Rc<RefCell<bool>> {
&Rc::new(RefCell::new(self.0))
}
fn copy_ref(&self) -> &Rc<RefCell<bool>> {
Rc::clone(self.get_ref())
}
}
fn main() {
let a = A(true);
a.copy_ref();
}
and I received warning complaining about the Rc::clone function not getting a reference:
error[E0308]: mismatched types
--> src/main.rs:12:9
|
12 | Rc::clone(self.get_ref())
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected reference, found struct `std::rc::Rc`
|
= note: expected type `&std::rc::Rc<std::cell::RefCell<bool>>`
found type `std::rc::Rc<std::cell::RefCell<bool>>`
I have been working on this all night but I cannot get it to work. The method get_ref is already typed as returning &Rc<RefCell<bool>>, but why would the compiler give the error?
The error is not talking about the argument you put into Arc::clone(), but the whole expression Rc::clone(...) which has a different type (Rc<...>) than the return type of your function (&Rc<...>).
If you were passing a wrong argument to Rc::clone, it would like look this:
--> src/main.rs:13:19
|
13 | Rc::clone(false)
| ^^^^^ expected reference, found bool
|
= note: expected type `&std::rc::Rc<_>`
found type `bool`
So the naive way to fix the type error is to write &Rc::clone(...) instead. Then the last expression of your function has the same type as your function's declared return type. But as you will notice, you will get other errors afterwards.
Let's take a step back to see that your approach is flawed here. For the most important point, please see "Is there any way to return a reference to a variable created in a function?". Spoiler: you really don't want to. So constructs like your get_ref() just don't make sense, as you return a reference to a variable you create inside your function (a variable of type Rc).
In your case the direct solution is probably pretty simple: just remove the reference. Rc<T> is already a pointer/reference type, so there is no need (in general) to have a reference to it.
However, since you are using Rc, you are probably interested in reference counting. So in that case, you probably shouldn't create a new Rc every time the function is called. Otherwise you could end up with a bunch of Rcs with reference count 1, which is not really the point. So instead, your type A should already store an Rc<RefCell<bool>>.
But all I'm doing here is guessing what you actually want to do which is not clear from your question. Maybe you can ask a different question, or add the information to this question, or explain this in the comments.
I am attempting to return a function pointer, which is located inside a for loop, from a function located in an impl of a struct.
fn locate_func(&self, string: &str) -> fn() -> bool {
let mut func;
for alt in &self.alts {
return alt.func;
}
}
There will be an if statement inside the for loop in the future, but as I am testing things at the very moment, it looks rather generic, and somewhat illogical.
The above code in my mind, is supposed to return the pointer to alt.func(), which clearly is a pointer, as it tells me so should I remove the return and semicolon of that line.
error[E0308]: mismatched types
--> src\main.rs:42:3
|
42 | for alt in &self.alts
| ^ expected fn pointer, found ()
|
= note: expected type `fn() -> bool`
= note: found type `()`
Above is the error that is caused upon running locate_func(). I am clearly missing something as the aforementioned code is not working properly. Any hints?
Your for-loop is the last expression inside the function. The compiler expects the last expression to evaluate to the return type. But all loops evaluate to () (unit or void), so the compiler has a classic type mismatch there.
The correct question to ask yourself is: what would happen if the return inside of the loop wouldn't be executed (for example, because the loop isn't executed at all, because self.alts is empty)? This would lead to problems, wouldn't it?
So you have to return a valid object after the for-loop to cover that case. But if you are certain that the spot after the loop will never be reached you can use unreachable!(); to tell the compiler what you already know. However, if the program will reach this spot, it will panic! So better make sure, you know for certain how the program behaves.