What can be done with Rust's generic FromStr object? - rust

Rust's str class has a parse method that returns a FromStr object. parse is templated, and so the type that's being parsed from the str can be manually specified, e.g. "3".parse::<i32>() evaluates to (a Result object containing) the 32-bit int 3.
But failing to specify the type does not seem to be an error in itself. Instead, I get an error when trying to print the resulting (generic/unspecified) FromStr object:
let foo = "3".parse();
match foo
{
Ok(m) => println!("foo: {}", m),
Err(e) => println!("error! {}", e)
}
This does not give an error on the first line; instead, I get the following error:
<anon>:24:12: 24:13 error: unable to infer enough type information about `_`; type annotations or generic parameter binding required [E0282]
<anon>:24 Ok(m) => println!("foo: {}", m),
(Here, line 24 is the line with the Ok(m).)
So what is m here? Or is the "unable to infer enough type information" error actually due to the fact that parse in fact can't be called without a type specifier, and the compiler just doesn't catch the error until the first line where the resulting Ok type is actually used?

Rust's str class has a parse method that returns a FromStr object.
Stop right here, this is your error.
parse does not return a FromStr object; FromStr is a trait which can be thought of as an abstract class if you come from an OO background, and you cannot return an object with an abstract type: it's abstract!
What parse does return, thus, is an instance of some type T which must implement the FromStr interface.
But failing to specify the type does not seem to be an error in itself. Instead, I get an error when trying to print the resulting (generic/unspecified) FromStr object
Because there cannot be such generic/unspecific FromStr object. A concrete type must be inferred (from context) or explicitly spelled out, and this type must implement FromStr.
So what is m here?
Only you know what it should be, the compiler does not, and thus complain that it does not know what to do :)
Or is the "unable to infer enough type information" error actually due to the fact that parse in fact can't be called without a type specifier, and the compiler just doesn't catch the error until the first line where the resulting Ok type is actually used?
Basically.
Except that it's not so much that the compiler doesn't catch the error until the first line where the resulting Ok is used, and more that the compiler considers the full function at once when inferring types. From the point of view of the compiler, whether the actual clue to infer the type comes immediately or comes 50 lines down does not matter, it only needs to be present in the current function body.
It might lead to the complaint about the lack of type originating in an odd place from the developer point of view; this is one of the downfalls of type inference. On the other hand, the compiler just cannot know where YOU would prefer to put the annotation. There are after all many possibilities:
// Example 1: immediately specifying the type
fn main() {
let foo = "3".parse::<i32>();
match foo
{
Ok(m) => println!("foo: {}", m),
Err(e) => println!("error! {}", e)
}
}
// Example 2: partially specifying the result type
// Note: the "_" is deduced to be std::num::ParseIntError because
// this is how `FromStr::Err` is defined for `i32`.
fn main() {
let foo: Result<i32, _> = "3".parse();
match foo
{
Ok(m) => println!("foo: {}", m),
Err(e) => println!("error! {}", e)
}
}
// Example 3: specifying the result type of unwrapping
fn doit() -> Result<(), std::num::ParseIntError> {
let foo: i32 = try!("3".parse());
println!("foo: {}", foo);
Ok(())
}
fn main() {
match doit()
{
Ok(_) => (),
Err(e) => println!("error! {}", e)
}
}
// Example 4: letting the type be inferred from a function call
fn callit(f: i32) {
println!("f: {}", f);
}
fn main() {
let foo = "3".parse();
match foo
{
Ok(m) => callit(m),
Err(e) => println!("error! {}", e)
}
}

It's just not clear what m is here, as there isn't enough information to say. Is it an i32? A u64? Nobody, including Rust, can know.
You need to do something to help figure out what type it is. Either pass it to a function expecting a specific type, or annotate it such that it can be determined what type it should be.

Related

Matching on a enum String

I am learning Rust and I am trying to write a simple IP handling function
enum IpAddr{
v4(u8,u8,u8,u8),
v6(String),
}
impl IpAddr{
fn write(&self){
match *self {
IpAddr::v4(A,B,C,D) => println!("{}.{}.{}.{}",A,B,C,D),
IpAddr::v6(S) => println!("{}",S)
}
}
}
The v4 matches fine, but I get the following build error on the 2nd one
error[E0507]: cannot move out of self.0 which is behind a shared reference
move occurs because _S has type String, which does not implement the Copy trait
How can I match on a enum with a String attached?
Its complaining because you are trying to copy self by dereferencing, but IpAddr contains a String which is not copy-able.
Remove the dereference and it should work as expected
match self {
IpAddr::v4(A,B,C,D) => println!("{}.{}.{}.{}",A,B,C,D),
IpAddr::v6(S) => println!("{}",S)
}
I'm not sure why you're matching on *self, as it does not appear to be necessary, but if you really need to do that for some reason, you can also solve the problem by using the ref keyword with your variable (ref s), which will cause the value to be borrowed instead of moving it.
enum IpAddr{
V4(u8,u8,u8,u8),
V6(String),
}
impl IpAddr{
fn write(&self){
match *self {
IpAddr::V4(a,b,c,d) => println!("{}.{}.{}.{}",a,b,c,d),
IpAddr::V6(ref s) => println!("{}", s)
}
}
}
Playground

How to match on a struct with private fields in Rust?

I have a function which returns an error type that contains private fields. I want the caller to match on this error type, and when matched, print a hardcoded error message. That's because this error type already has a specific meaning, so I'm not interested in the message that err.display() would return.
However, the function could return a different error type in the future. If that happens then I want existing callers to get a compilation error, so that they can update the error message that they print.
However, if callers match on Err(_), then a change in the error type doesn't result in a compilation error.
How do I solve this? One way would be to match on the specific error type, but that doesn't seem possible if the error type has private fields.
Here's example code:
use std::ffi::{CString, NulError};
fn create_cstring() -> Result<CString, NulError> {
return CString::new("hello");
}
fn main() {
match create_cstring() {
Ok(val) => println!("Output: {:?}", val),
Err(_) => println!("Error: input contains forbidden null bytes"),
};
}
Suppose I change create_cstring()'s signature and make it return something other than NulError. How do I make the match block fail to compile?
Use .. to match the private fields:
use std::ffi::{CString, NulError};
fn create_cstring() -> Result<CString, NulError> {
return CString::new("hello");
}
fn main() {
match create_cstring() {
Ok(val) => println!("Output: {:?}", val),
Err(NulError{..}) => println!("Error: input contains forbidden null bytes"),
};
}
Playground

Pattern matching on same arm with different types

I wonder if there is a way to simplify the following pattern match arms when two or more different enum's types have the same data member or same function.
(if not it will be nice to explain why)
UPDATE:
as requested a more accurate example of what i want (forgive me for confusing data member access with function) (try it online):
struct Point<T> {
x: i32,
y: T,
}
enum Record {
V4(Point<i64>),
V6(Point<i32>),
}
fn get_record() -> Record {
Record::V4(Point{ x: 1, y: 1})
}
fn main() {
let x = match get_record() {
Record::V4(r) => r.x,
Record::V6(r) => r.x,
};
println!("{}", &x);
// this will not compile
// let rec = get_record();
// println!("{}", rec.x);
// this will not compile either
// note: if V4 Point was i32 it will compile & run
// let rec = get_record();
// let x = match get_record() {
// Record::V4(r) | Record::V6(r) => r.x,
// };
}
Original Post:
use std::net::IpAddr;
use std::str::FromStr;
fn main() {
let v4_or_v6 = IpAddr::from_str("1.2.3.4").unwrap();
// match expression, both arms only differ by 1 char
let s = match v4_or_v6 {
IpAddr::V4(ip) => ip.to_string(),
IpAddr::V6(ip) => ip.to_string(),
};
println!("{}", &s);
// not working:
// let s2 = match v4_or_v6 {
// IpAddr::V4(ip) | IpAddr::V6(ip) => ip.to_string(),
// };
// println!("{}", &s2);
}
I understand that the underlying call to to_string() has different implementation for Ipv4 than Ipv6 but i think the compiler can be smart enough to handle this (am i wrong?)
trying to compile with the commented out code results in compilation error (try it online):
Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
--> src/main.rs:16:37
|
16 | IpAddr::V4(ip) | IpAddr::V6(ip) => ip.to_string(),
| ^^ expected struct `std::net::Ipv4Addr`, found struct `std::net::Ipv6Addr`
|
= note: expected type `std::net::Ipv4Addr`
found type `std::net::Ipv6Addr`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.
error: Could not compile `playground`.
The working code de-sugars to:
let s = match v4_or_v6 {
IpAddr::V4(ip) => <Ipv4Addr as ToString>::to_string(&ip),
IpAddr::V6(ip) => <Ipv6Addr as ToString>::to_string(&ip),
};
Even though the statements look the same, they are different functions and in each branch it is known statically which to_string is going to be used. To get this to work in a single match arm, you would have to somehow produce a trait object from the pattern match, so that each ip has the same type (i.e. &dyn ToString). Currently there isn't a way to do that and I haven't seen any proposal like it.
It's pretty common to see identical-looking match arms, where the same trait method is called on each, even in the rustc project. This is just how it is, for now.
If you have an enum where each variant holds types that implement the same traits, it might be convenient to implement the traits on the enum and delegate to the inner types. If you don't have a trait but your types have common structure (as in the x, y fields in the struct of your updated post), then you can provide an accessor on the enum:
impl Record {
fn x(&self) -> i32 {
match self {
Record::V4(Point { x, .. }) => *x,
Record::V6(Point { x, .. }) => *x,
}
}
}
While this is basically the same thing, it means you can write it once instead of everywhere that you need to access x:
let rec = get_record();
let x = get_record().x();
Note that IpAddr already does this so, in your original code, you could have avoided the match altogether with:
let s = v4_or_v6.to_string();

What is unwrap in Rust, and what is it used for?

I have this code that uses .unwrap():
fn main() {
let paths = std::fs::read_dir("/home/user").unwrap();
for path in paths {
println!("Name: {}", path.unwrap().path().display());
}
}
After looking at the definition of unwrap,
pub fn unwrap(self) -> T {
match self {
Ok(t) => t,
Err(e) => unwrap_failed("called `Result::unwrap()` on an `Err` value", e),
}
}
And the signature of read_dir
pub fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir>
Am I correct in understanding that unwrap returns the T type that is passed in Result?
In Rust, when you have an operation that may either return a T or fail, you will have a value of type Result<T,E> or Option<T> (E will be the error condition in case of an interesting error).
The function unwrap(self) -> T will give you the embedded T if there is one. If instead there is not a T but an E or None then it will panic.
It is best used when you are positively sure that you don't have an error. If that is not the case usually it is better either pattern-match the error or use the try! macro ? operator to forward the error.
In your example, the call to read_dir() returns a io::Result<ReadDir> because opening the directory might fail. And iterating the opened directory returns multiple values of type io::Result<DirEntry> because reading the directory might also fail.
With try! ? it would be something like this:
fn try_main() -> std::io::Result<()> {
let entries = std::fs::read_dir("/home/user")?;
for entry in entries {
println!("Name: {}", entry?.path().display());
}
Ok(())
}
fn main() {
let res = try_main();
if let Err(e) = res {
println!("Error: {}", e);
}
}
Look how every error case is checked.
(Updated to use ? instead of try!(). The macro still works, but the ? is preferred for new code).
The problem is that reading a line from a file produces a potential error type. The type is
Result<String,std::io::Error>
Result is an enum. There are two potential values in the Result, they are used for error handling and management. The first value is Err. If Err is populated, there was an error in the function that was called. The other potential selection is Ok. Ok contains a value.
enum Result<T, E> {
Ok(T),
Err(E),
}
Enum is a complex type, rather than a single value. To get the value we are looking for, we use unwrap() to unwrap the value.
unwrap() is used here to handle the errors quickly. It can be used on any function that returns Result or Option (Option is also enum). If the function returns an Ok(value), you will get the value. If the function returns an Err(error), the program will panic.

Err doesn't accept argument that has been converted "to_string"

I am trying to return an error with a formatted message.
let msg = format!("wrong num of dimensions: {}", dimensions.len()).to_string();
return Err(msg);
dimensions is a Vec<i32> that is supposed to have a length of 3. If it doesn't, I want to raise this error. However, I am getting this compiler error:
Compiling advent02 v0.1.0 (file:///home/garrett/notes/rust/advent/advent02)
src/main.rs:19:24: 19:27 error: mismatched types:
expected `&str`,
found `collections::string::String`
(expected &-ptr,
found struct `collections::string::String`) [E0308]
src/main.rs:19 return Err(msg);
^~~
It looks to me like it wants an &str type when I am giving it a String type. I have tried to do this from this example but get borrow errors because the returned string borrow goes out of scope.
I don't understand because this page is littered with examples that do Err(err.to_string()), and they don't seem to have a problem.
The return type of your function is Result<T, &str>, that's why the compiler is complaining. You create a String instance, take a reference to it and try to return. When the function returns, the String instance is freed and your returned reference would be invalid (a dangling pointer).
I'd change the signature of the function to return a Result<T, String>. That way, you move the string out of the function without freeing it.
Here is a complete Rust program that shows the behavior you wanted:
fn test() -> Result<u32, String> {
let msg = format!("Error code {}.", 123).to_string();
return Err(msg);
}
fn main() {
match test() {
Ok(num) => println!("Okay: {}", num),
Err(msg) => println!("Error: {}", msg),
};
}
This compiles without warnings or errors and prints "Error: Error code 123." when run.
The important thing here is to declare the return type of the function that is returning the error code. If you don't declare the return type, then the Rust compiler apparently does a bad job of figuring out what the return type should be, and you get errors (but the errors I get from rustc 1.5.0 are different from yours). This might actually be a bug in the Rust compiler; you could report this to the developers of the language and see what they have to say.
The Err thing that you used is just a type constructor for std::result::Result. So the return type of the function should be some type of Result.

Resources