Clarifying how std::io::stdin() works

I'm a newbie working through the book and other resources as I learn to use the Rust language. This question is all about clarification of how Rust works.

I've been attempting to understand how using a crate or module relates to how I write my code and am confused. For example, I created a function to handle inputting integers so that I don't have to reinvent the wheel every time I need to ask the user for a value. I include the line

    use std::io::stdin;

at the top of my code and call my function in the body of fn main(). In the body of my input function, the key line is:

        std::io::stdin().read_line(&mut input).expect("Failed to read line.");

My question is why the repetition? My assumption going into this has been that when I include the use std::io::stdin; line at the beginning of my program, I should be able to access all the features contained in that library. By my way of thinking I should be able to eliminate the std::io::stdin() part of the line above because I already included it in the use statement at the beginning of my code. I experimented with keeping only the stdin(), but nothing works except what you see here. I'm hoping that someone can help me see how all this fits together so that I can get a better handle on how Rust works.

In the meantime, I'll keep working through my learning resources.

Thanks.

std::io::stdin isn't a library, it's a function. use lets you refer to it by the shorter name stdin instead of having to specify the full path std::io::stdin. Meaning you can change the line in question to stdin().read_line.... If that doesn't work, the use and line might be in different modules. You need the use in the module where you want to call stdin().

You can't drop the stdin() call no matter what you use, because you need to call it in order to call read_line on its return value.

2 Likes

use imports are scoped to the current module. If you do use std::io::stdin in one file, it won't affect what's in scope in another file. You have to do it in both files if you want to use stdin() without qualification in both places.

you and me. both. i am kinda expecting to find a saner way to get user input in Rust but it seems it would come after a few versions of the compiler. to me, if there is a "println!()" for output why isn't there a counterpart for input?

it's not a matter of how Rust works, but how people thinks. if you have a left, there is a right. an up, and a down. a light, and a dark. i don't know what you call it (visual learning?) but that's how i think.

Ok, so I thought I experimented with doing just the stdin() all by itself, but I guess not. I just now played around with it and it works fine. Thanks for your input.

@ [Heliozoa], I see what you are saying about stdin() being a function, but what about the std::io part? Couldn't that properly be called a "library". BTW, what is the distinction between a "library", a "module", and a "crate"? I'm really not clear on that.

In std::io::stdin "std" is a crate or library, "io" is a module, and "stdin" is an item (in this case, a function)

Why do you think there are no devices capable of producing cow (animal, not Rust's Cow) from mince?

It wouldn't come. Ever. In all languages which have such capabilities (C, C++, Pascal (Delphi today), etc) they are only every used in simple tutorials in colleges.

Rust just decided that adding facility which would require separate tutorial which would teach people to stop using these (like happens with C or C++) is simply not worth it.

Data in Rust program (and in most high-level languages) exist in highly structured form. It's separated among hundreds (in small programs) or billions (for complex programs) variables, they are connected in some elaborate fashion, etc.

File is just sequence if bytes. All these objects are grinded to the mince when output is happening. To get that data back and do that correctly you need to do that carefully.

It's easy to grind data to produce some kind of text output (hey, even C++ got Rust-style robust facilities with all these {}, {:04x} not so long ago… as far as I/O is concerned C++ is slowly moving in the direction of Rust, not the other way around), but not easy to parse text input and correctly interpret it.

If you want symmetry then look on serde. But you wouldn't be able to use it to work with unstructured input. It's just not useful.

2 Likes

Ok, that makes sense, but what is the difference between a crate and a module?

A module is just a namespace inside a crate[1]. You can create as many modules as you want inside a crate and they all share the same dependencies and are built together.

Most of the time when you're writing Rust code using existing crates and modules the distinction doesn't matter all that much though to be honest. The primary difference is that crates are always in scope in any module as long as they're a dependency of your crate, and the modules inside them are not.


  1. as an analogy, you might have a hard drive with a bunch of folders on it. The hard drive would be the crate and the folders would be the modules ↩︎

1 Like

Alright, now bear with me, please. I get that within the std crate io is a module and that stdin() is a function within that module. So, what I want to do is just declare use std at the top of my code and then just assume that all the modules and functions included in std are available. That doesn't work, I've tried it, but I'm not sure why Rust won't allow it. Could you address that? Thanks.

use std; is not use std::*;. The former makes std available (and is thus redundant, since std is always available), while the latter makes every item inside std available.

1 Like

This wouldn't make much sense because std::result::Result and std::io::Result are different.

1 Like

Well use std::* is just what I wanted to do, but when I tried it the compiler wouldn't accept it. So clearly Rust requires us to specify both the module and the target function in our use statements. In my mind this reduces the usefulness of having crates. Can anyone explain to me why Rust requires this?

use std::* imports all of the items in the crate root. That means you could use io::stdin() but not stdin().

As @VorfeedCanal pointed out above, there are types and functions whose names would conflict if they were all in the same namespace. Since there isn't an obvious way to pick a winner when names conflict, a syntax for importing all items in a crate regardless of where they are in the crate's module hierarchy wouldn't make a whole lot of sense.

1 Like

I have no idea what you were doing, but it's clearly accepted.

If you're thinking of Ruby modules or C/++ headers, then you need to forget all that. The following code:

use std::io::stdin;

fn main() {
  for name in stdin().lines() {
    println!("hello {line}");
  }
}

and this code:

fn main() {
  for name in std::io::stdin().lines() {
    println!("hello {line}");
  }
}

are identical. The only thing use does is let you use a shorter name (or rename with as, and there's traits, but don't worry about that yet)

There's a lot more to learn about how to create and use modules and crates, but don't worry about any of that yet, the only thing you need to know is that you automatically get a std module that is available anywhere in your code (this is essentially what being a crate means), which has all the standard library stuff, broken up into more modules by functionality.

3 Likes

Thank you, everyone. All that discussion really helped me get a broader concept of how crates and modules work and how to use them in my programs. I appreciate your help.