Text file manipulation (io, fs,...)

Cheers all,

I'm new to Rust and want to start a new desktop project using Rust (floating point calculation-intensive), where I intend to make use of the multi-threading Rust capabilities to split tasks in parallel in a 8-core processor. I need to read/write simple text files. I've noticed that the io system in Rust has undergone major changes recently (with the approximation of the launching of version 1.0.0) -- I read some threads in the forums and I got a bit confused...

I tried to create/write-into a text file using a simplification of an example from std::fs::File. I ran it with the beta1.0.0 and with the current nightly on Windows-7 64 bits. Get an error. Below are the code (4 lines :slight_smile: ) and the error. Tried to remedy the thing in a couple other ways (e.g. using old_io, googling some examples) but always got into errors.

I would appreciate very much both a pointer to my mistake and a pointer to up-to-date docs about the io and fs systems that will be stable in Rust 1.0.0.

Thanks in advance and regards

Jose

//////// CODE //////

//use std::io::prelude::*;
use std::fs::File;

fn main() {
let mut f = File::create("foo.txt");
f.write_all("Hello, world!");
}

//////////////// ERROR ////////////////

rustc t2.rs
t2.rs:7:4: 7:30 error: type core::result::Result<std::fs::File, std::io::error::Error> does not implement any method in scope named write_all
t2.rs:7 f.write_all("Hello, world!");
^~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to previous error
Exit code: 101

Hi jasa,

Have a look at the std::fs::File API. If you scroll down to the create() function, you'll notice that create() returns a Result<File>, and not a File. What you'll want to do is wrap your create() method call with the try!() macro. That macro automatically unwraps the Result if everything went OK, otherwise it will return the error that was called. See below:

use std::fs::File;

fn main() {
let mut f = try!(File::create("foo.txt"));
f.write_all("Hello, world!");
}

That won't actually work because you can't return anything from main. You should either handle the error:

use std::io::Write;
use std::fs::File;
let mut f = match File::create("foo.txt") {
    Err(e) => {
        println!("Couldn't open foo.txt");
        return;
    },
    Ok(f) => f,
};
f.write_all(b"Hello world!");

Or assert that there is no error and panic if there is:

use std::io::Write;
use std::fs::File;
let mut f = File::create("foo.txt").unwrap(); // Panics (see the docs) if create failed.
f.write_all(b"Hello, world!");
2 Likes

Thank you zcdziura,

Unfortunately I still get errors when compiling (with Rust 1.0.0 beta) your code snippet. BTW, the error is the same in Rustc 1.0.0-beta on Linux Mint 17.1 over a VirtualBox.

Thx for your help
Jose

/////////////////////////////
:5:8: 6:42 error: mismatched types:
expected (),
found core::result::Result<_, _>
(expected (),
found enum core::result::Result) [E0308]
:5 return $ crate:: result:: Result:: Err (
:6 $ crate:: convert:: From:: from ( err ) ) } } )
:1:1: 6:48 note: in expansion of try!
t2.rs:14:13: 14:43 note: expansion site
t2.rs:15:3: 15:29 error: type std::fs::File does not implement any method in scope named write_all
t2.rs:15 f.write_all("Hello, world!");
^~~~~~~~~~~~~~~~~~~~~~~~~~
t2.rs:15:29: 15:29 help: methods from traits can only be called if the trait is in scope; the following trait is implemented but not in scope, perhaps add a use for it:
t2.rs:15:29: 15:29 help: candidate #1: use std::io::Write
error: aborting due to 2 previous errors

Thank you stebalien,

Unfortunately I still get errors when compiling your code (with Rust 1.0.0 beta). The write_all method still is not recognized? Output below.

Thx for your help
Jose

////////////
t2.rs:23:3: 23:28 error: type std::fs::File does not implement any method in scope named write_all
t2.rs:23 f.write_all("Hello world!");
^~~~~~~~~~~~~~~~~~~~~~~~~
t2.rs:23:28: 23:28 help: methods from traits can only be called if the trait is in scope; the following trait is implemented but not in scope, perhaps add a use for it:
t2.rs:23:28: 23:28 help: candidate #1: use std::io::Write
error: aborting due to previous error

You need a use std::io::Write; (the method write_all comes from the trait Write, as you can see in the docs at File in std::fs - Rust)

Thank you very much arielb1, that does the trick. I thought "write_all" was accessible with "use std::fs::File" because in the example in std::fs::File ( -- the link you suggest --) there is no "use std::io::Write" writte. Perhaps the docs are not fully updated?

Thanks again

Jose

update: Oops, the line " use std::io::prelude::*; " in the examples which I commented out also does the trick...

Oh, good to know! Thank you your answer!

Yeah there needs to be documentation about this behavior, IMO it is a common problem that is talked about very little.

The documentation on traits explicitly discusses that they need to be in scope for methods related to them to work.

1 Like

Before I commented, I look at the book under the 'Traits' section and did not see any mention of the behaviour.

It starts with

First, traits must be used in any scope where you wish to use the trait's method. So for
example, this does not work:

Well I will be damned. I think it might be a good idea to use the std library in the docs, because it might not be immedeately obvious what that means, i know i glossed over it.

More of it will be added with the revised TOC. But the hard part is the psychic abilities needed to determine what gets skipped :wink:

More seriously, there's also a lack of headings, I'm trying to add more, so there's more links.

1 Like

haha I meant it to say

i know i glossed over it, so probably doesn't mean much

But it came out completely wrong!

I like to think that extension traits and how they interact is one of the worst (and if not my least favorite) gotchas in Rust. I think documenting the need for the use std::io::prelude::* is sufficient, other than just it being in the docs. just my 2cents, keep up the good work!

Could you elaberate why can not return anything from main? Thanks.

main is just a regular function with the signature fn() -> () so you can't return anything other than ().

I see. the return type in try! have to match the return type of the outer fn, due to try1 is a macro, not a fn.

Yes. More specifically, try! doesn't have a return type, it contains a return statement. try! expands to:

match $expr {
    Ok(v) => v,
    Err(e) => return Err(::std::convert::From::from(e)) // Implicitly convert the error
}
1 Like

I'm also running 1.0.0-beta and stebalien's code sample isn't working.

The full text of my main.rs is:

use std::fs::File;
use std::io::prelude::*;

fn main() {
    let mut f = File::create("foo.txt").unwrap();
    f.write_all("Hello, world!");
}

But I'm getting this error:

src/main.rs:6:14: 6:29 error: mismatched types:
 expected `&[u8]`,
    found `&'static str`
(expected slice,
    found str) [E0308]
src/main.rs:6     f.write_all("Hello, world!");
                              ^~~~~~~~~~~~~~~

I'm not sure how to coerce that string into a &[u8]. Or what that even is; I'm having trouble finding it mentioned in the docs.