Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stdio, Read, Write and Files #482

Open
UsairimIsani opened this issue Jul 29, 2020 · 12 comments
Open

Stdio, Read, Write and Files #482

UsairimIsani opened this issue Jul 29, 2020 · 12 comments
Labels
A-exercises Area: Exercises C-new-exercise Category: New Exercise

Comments

@UsairimIsani
Copy link

Add Exercises on Stdio, Read, Write and Files.

@shadows-withal shadows-withal added A-exercises Area: Exercises C-feature-request Category: Feature Request labels Jul 29, 2020
@calvinbrown085
Copy link
Contributor

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

@seeplusplus
Copy link
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
Copy link
Contributor

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

@samusz
Copy link

samusz commented Feb 20, 2022

May be also these common use case :

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

@mo8it
Copy link
Contributor

mo8it commented Jul 10, 2024

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

Open to pull requests :D

@mo8it mo8it added C-new-exercise Category: New Exercise and removed C-feature-request Category: Feature Request labels Sep 2, 2024
@frroossst
Copy link
Contributor

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

@mo8it
Copy link
Contributor

mo8it commented Sep 3, 2024

@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
Copy link
Contributor

frroossst commented Sep 5, 2024

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
Copy link
Contributor

mo8it commented Sep 16, 2024

@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
Copy link
Contributor

frroossst commented Oct 13, 2024

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.

@frroossst
Copy link
Contributor

Should I make a pull request to add a new exercise?

@mo8it
Copy link
Contributor

mo8it commented Nov 12, 2024

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.

It would be much better to let users write their code in normal functions that are then used inside tests. These tests shouldn't be changed by users. Before running the function, the tests would make sure that the file either exists or not (depending on what the user should expect).

@frroossst you are welcome to create a PR. I will start reviewing and merging PRs related to new exercises in December. Then I can release Rustlings v7 in the new year :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-exercises Area: Exercises C-new-exercise Category: New Exercise
Projects
None yet
Development

No branches or pull requests

7 participants