Skip to content

Stdio, Read, Write and Files #482

@UsairimIsani

Description

@UsairimIsani

Add Exercises on Stdio, Read, Write and Files.

Activity

calvinbrown085

calvinbrown085 commented on Sep 21, 2020

@calvinbrown085
Contributor

I can try and take files if that would be okay!

seeplusplus

seeplusplus commented on Nov 5, 2020

@seeplusplus
Contributor

I'd like to help close this. What kinds of exercises would we like to see? With @calvinbrown085 's work we have reading from a file (files1). I could see adding something like:

  • Files: files2, writing/amending
  • Stdio: stdio1, capturing user input from stdin and writing it out to stdout

I'd be happy to commit to creating these exercises (as well as any others you folks suggest).

calvinbrown085

calvinbrown085 commented on Nov 15, 2020

@calvinbrown085
Contributor

IMO, writing a new file would be a good exercise, I also agree that your stdio ones would be good too!

samusz

samusz commented on Feb 20, 2022

@samusz

May be also these common use case :

  • write to stderr
  • write/append to log file
  • write a utf-8 file
  • write to stream ?
mo8it

mo8it commented on Jul 10, 2024

@mo8it
Contributor

I think that we can add a whole IO section with at least 2 exercises :)

Open to pull requests :D

added and removed on Sep 2, 2024
frroossst

frroossst commented on Sep 3, 2024

@frroossst
Contributor

I can try and work on a new io section if this is still wanted

mo8it

mo8it commented on Sep 3, 2024

@mo8it
Contributor

@frroossst Go ahead!

I think that we can have 3 small exercises about dealing with files:

  1. Opening and reading a file. Maybe just count the number of lines? The solution should show both manually opening and reading the file and using fs::read_to_string.
  2. Creating a file and writing to it. The file should be truncated if it already exists. The solution should show both manually opening and writing the file and using fs::write.
  3. Appending to an existing (log) file. It should be created if it doesn't already exist. No truncation.

In all of these exercises, the user should complete the body of a function which takes a path as argument. Input files should be created in tests and the path is then passed to the user function.

In the last exercise with appending, we can teach about the Write trait by letting the user use the file with a function that expects something implementing Write. We can test that function with a buffer of bytes then to demonstrate the Write abstraction.

I think that an exercise about stdin would be too confusing because the user can't test it with some input in the terminal. Rustlings captures the whole output and shows it after the exercise terminates. We could write a test that calls the exercise and passes some input as stdin, but the user can't really test it in the terminal. So I will skip stdin.

stdout and stderr are easy with print(ln)! and eprint(ln)!. No need for an exercise for that. Using std::io::stdout and maybe locking manually is too advanced. Out of the scope of Rustlings.

frroossst

frroossst commented on Sep 5, 2024

@frroossst
Contributor

I'm a little confused as to what is meant by manually opening/reading the files manually.

I wrote a little sample, please tell me if it meets expectations.

fn main() {}

#[cfg(test)]
mod test {

    use fs;

    #[test]
    fn can_you_read_in_rust() {
        let content = fs::read("./ferris.txt").unwrap();
        // let _ = fs::???("./ferris.txt").unwrap();
        
        let expect: Vec<u8> = vec![
            102, 101, 114, 114, 105, 115, 10,
        ];
        assert_eq!(expect, content)
    }

    #[test]
    fn read_a_string_directly() {
        // you might've noticed in the previous test, that the file
        // reads in a Vec<u8>, hey! that doesn't look like a string
        // No, no it doesn't, afterall evrything in a computer is bits
        // and bytes, but, Rust provides us with a convienient method
        // to read strings directly
        let content = fs::read_to_string("./ferris.txt").unwrap();

        assert_eq!("ferris", content)
    }
}
mo8it

mo8it commented on Sep 16, 2024

@mo8it
Contributor

@frroossst

With manually opening and then reading a file, I mean something like this:

let mut file = File::open("Cargo.toml")?;
let mut content = String::new();
file.read_to_string(&mut content)?;
println!("{content}");

The alternative is to use the specialized, but less flexible functions from std::fs:

let content = fs::read_to_string("Cargo.toml")?;
println!("{content}");

Some points:

  • I don't think that we need to teach how to read bytes. It is not that different from reading strings.
  • Always add a TODO: … where a user needs to do something.
  • Try to avoid syntax errors in the exercise. So no fs::???(). Instead, I prefer to write a comment describing what the user should do. In this case, something like "TODO: Use a function from std::fs to read the file ferris.txt".
  • Avoid long comments.
frroossst

frroossst commented on Oct 13, 2024

@frroossst
Contributor

Hey, @mo8it sorry school has been super busy lately, didn't get much free time.

fn main() {}

#[cfg(test)]
mod test {

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

    #[test]
    fn can_you_read_in_rust() {
        // the user enters the name of the file to open
        // the unwrap will be missing for the File::open
        let mut file = File::open("ferris.txt").unwrap();
        let mut content = String::new();

        // the user will fill in the read_to_string method
        file.read_to_string(&mut content).unwrap();

        assert_eq!(content.len(), 10);
    }

    #[test]
    fn can_you_write_in_rust() {
        let msg = "hello world!";

        // here the user will enter the path of the file
        let path = "ferris.txt";

        // the user will fill the create method
        let mut file = File::create(path).unwrap();

        // the unwrap will be missing here
        file.write_all(msg.as_bytes()).unwrap();
    }

    #[test]
    fn can_you_append_in_rust() {
        todo!()
    }


}

I did have a question, how would we verify that a file called ferris.txt would exist when testing read or how do we verify that the user created a file. The obvious thing is when the test start create the necessary file and in the assert check if the file exists for the writing test.

I was wondering if there were better, more elegant or more idiomatic ways to deal with this.

3 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      Participants

      @samusz@manyinsects@calvinbrown085@UsairimIsani@seeplusplus

      Issue actions

        Stdio, Read, Write and Files · Issue #482 · rust-lang/rustlings