# A read!() macro for std::io?

**URL:** <https://internals.rust-lang.org/t/a-read-macro-for-std-io/15950>\
**Category:** language design\
**Created:** [January 12, 2022, 10:15am UTC](https://internals.rust-lang.org/t/a-read-macro-for-std-io/15950 "2022-01-12T10:15:32Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![T-O-R-U-S](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/t-o-r-u-s/32/8156_2.png) [@T-O-R-U-S](https://internals.rust-lang.org/u/T-O-R-U-S)\
**Post date:** [January 12, 2022, 10:15am UTC](https://internals.rust-lang.org/t/a-read-macro-for-std-io/15950/1 "2022-01-12T10:15:32Z")

</div>

## Proposal:

Currently, in order to read input, you have to go through a (subjectively) ugly set of motions; this new feature proposes a fix to that.

```rs
// Before
let mut input = String::new();
stdin().read_to_string(&mut input).unwrap();
// Remove mutability
let input = format!("{:.12}", input);
// After
let input = read!("{:.12}");

```

And let's say, you want to prompt the user for **what** you need them to input, then it becomes even worse; and because of this, I would also suggest a `prompt!` macro.

```rs
// Before
print!("Input your magic number: ");
stdout().flush().unwrap();

let mut input = String::new();
stdin().read_to_string(&mut input).unwrap();
// Remove mutability
let input = format!("{:.12}", input);

// ...

// After

let input = prompt!("Input your magic numbers" => "{:.12}");

// ...

```

This was all inspired by the `text_io` crate. I'm unsure how big the demand for this feature would be, but I saw it, and thought that it provided what Rust is supposed to be right after rock-solid-stable; high-level. This provides a high level abstraction to console I/O.

## Problems:

Currently, this does not give you the choice of whether you should `unwrap`, `expect` or handle each error individually via a `match` statement; thus, this would damage the idea of zero-cost abstractions, and it would also damage Rust's idea of bringing each error to the foreground. A straightforward solution would be to just simply return a `Result` enum

---

<div class="post-metadata">

**Author:** ![scottmcm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/scottmcm/32/2355_2.png) [@scottmcm](https://internals.rust-lang.org/u/scottmcm)\
**Post date:** [January 12, 2022, 11:02am UTC](https://internals.rust-lang.org/t/a-read-macro-for-std-io/15950/2 "2022-01-12T11:02:32Z")

</div>

You might be interested in the conversation in

> <https://github.com/rust-lang/rfcs/pull/3196>
>
> I think the best way to drive the addition of this function forward is to have a… dedicated RFC for it.
> 
> \[Rendered\](https://github.com/rust-lang/rfcs/blob/126e2bd1f6853ec2701cc041104917640f94bb2f/text/3196-inputln.md#readme)
> 
> Previous discussions:
> 
> \* https://github.com/rust-lang/rust/pull/75435
> \* https://github.com/rust-lang/rfcs/pull/3183 (related RFC that additionally proposes scan macros)

---

<div class="post-metadata">

**Author:** ![piegames](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/piegames/32/9140_2.png) [@piegames](https://internals.rust-lang.org/u/piegames)\
**Post date:** [January 14, 2022, 5:28pm UTC](https://internals.rust-lang.org/t/a-read-macro-for-std-io/15950/3 "2022-01-14T17:28:36Z")

</div>

Yeah, this topic comes up every once in a while. However, most of the time it's more a "I'd like to do X" with some amount of magic syntax and wishful thinking how things could be. But actual difficulty is to craft an API that works well for a wide variety of use cases.

I think most of us want this feature, or some variation of it. If you want to be the chosen one that brings us this gift, I suggest doing the following:

- Read up the existing discussions on the subject
- Implement a few prototypes and ideas as library. No compiler support is required for this feature, so there's no need for it being in `std` initially.
- Ideally, your library is so good that it gains traction and becomes the go-to tool for the use cases it aims to solve
- Do an RFC that proposes to lift the most commonly used features of your library into `std`. This should come with a great section analyzing other libraries on that subject, comparing benefits and tradeoffs.

Two more remarks: 1. If you're aiming for getting into `std`, keep the API purely read-only, i.e. don't do a "prompt"-style API. 2. I estimate the probability of this ever landing in `std` to be rather low. So your time is better spent trying to build a good crate that everybody uses than to get this into `std`. Think of `std` as a bonus, and not as the goal.

---

<div class="post-metadata">

**Author:** ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)\
**Post date:** [January 14, 2022, 9:24pm UTC](https://internals.rust-lang.org/t/a-read-macro-for-std-io/15950/4 "2022-01-14T21:24:41Z")

</div>

See also the recent

> **[Ezio](https://www.ncameron.org/blog/ezio/)**
>
> I made a small crate for easy-to-use IO in Rust. It is called ezio, and 0.1 is released today. It is a simple crate and it is ready to use now. I expect it will evolve somewhat on the way to a 1.0 release, but there is nothing

---

<div class="post-metadata">

**Author:** ![ExpHP](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/exphp/32/6208_2.png) [@ExpHP](https://internals.rust-lang.org/u/ExpHP)\
**Post date:** [January 14, 2022, 10:36pm UTC](https://internals.rust-lang.org/t/a-read-macro-for-std-io/15950/5 "2022-01-14T22:36:37Z")

</div>

At large, I have a knee-jerk reaction to STDIN-based text prompts, and have a predisposition against putting things in the standard library that aim to make this style of program easier to write. However, for the sake of this discussion I will try to avoid discussing their (de-)merits.

I will agree that `read_to_string`'s mutable argument makes it annoying to use. However, it's also the wrong tool for the job here, because it reads to EOF. At the very least this should be `BufRead::read_line`, but you can solve the whole mutability issue by using [`BufRead::lines`](https://doc.rust-lang.org/std/io/trait.BufRead.html#method.lines) instead.

What I find most puzzling and suspect here is that your primary motivation for introducing a macro seems to be to have a place to put this `"{:.12}"`. But why would you want to pad a string with spaces at the time you are reading it, rather then at the time it is _used_? If you merely wanted to remove mutability, `let input = input;` would suffice.

---

<div class="post-metadata">

**Author:** ![T-O-R-U-S](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/t-o-r-u-s/32/8156_2.png) [@T-O-R-U-S](https://internals.rust-lang.org/u/T-O-R-U-S)\
**Post date:** [January 15, 2022, 3:51pm UTC](https://internals.rust-lang.org/t/a-read-macro-for-std-io/15950/6 "2022-01-15T15:51:26Z")

</div>

`{:.12}` does not add padding; it limits the amount of characters in a text. It starts with a `.` because it's typically used to limit the precision of decimal numbers to a certain value. `3.141592` → `{:.4}` → `3.1416`, but it also works on strings.

And you are right, `let input = input;` would be enough, if you didn't also want to remove excess characters (I tried `String::with_capacity(12)`, it didn't work). But it's also ugly 🙂. I guess some of the blame would partially fall on me for not _also_ specifying that it not only removes mutability, but also achieves the desired formatting.

```rust
format!("{:.12}", "This text exceeds the character limit!")

// Out: "This text ex"

```

---

<div class="post-metadata">

**Author:** ![ExpHP](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/exphp/32/6208_2.png) [@ExpHP](https://internals.rust-lang.org/u/ExpHP)\
**Post date:** [January 15, 2022, 5:55pm UTC](https://internals.rust-lang.org/t/a-read-macro-for-std-io/15950/7 "2022-01-15T17:55:37Z")

</div>

Indeed, I misinterpreted the format string, but at the same time I feel like this goes to show just how uncommon I've found the need to use a precision specifier on a string type (or really, anything other than a float type) in rust. So it seems strange to see it being given front and center treatment here.

I'm not even sure how the precision specifier treats Unicode. Counts `char`s (i.e. scalar values), probably? If I really wanted a limit, I'd more likely impose a byte count (and error on excess), or maybe use the Unicode width crate if it is for display on a console (and truncate the excess).

---

<div class="post-metadata">

**Author:** ![Aloso](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/aloso/32/5039_2.png) [@Aloso](https://internals.rust-lang.org/u/Aloso)\
**Post date:** [January 18, 2022, 7:36pm UTC](https://internals.rust-lang.org/t/a-read-macro-for-std-io/15950/8 "2022-01-18T19:36:44Z")

</div>

And how is truncating the string to a specific length useful? I assume if you want to forbid inputs that are longer than 12 chars, you should show an error instead of silently truncating the string.

I think there's a misunderstanding: `String::with_capacity(12)` reserves 12 **bytes** , whereas `"{.12}"` truncates to 12 [**chars**](https://doc.rust-lang.org/std/primitive.char.html) (Unicode code points), so the string can be up to 48 bytes.

* * *

If this is meant as an optimization and you want to stop reading from stdin after a certain number of bytes, you can do something like

```rust
use std::io::{self, BufRead, Read};

fn read_line_or_bytes(len: usize) -> Result<String, io::Error> {
    io::BufReader::with_capacity(len, io::stdin())
        .take(len.try_into().unwrap())
        .lines()
        .next()
        .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "line missing"))?
}

fn main() {
    println!("{}", read_line_or_bytes(12).unwrap());
}

```

---

<div class="post-metadata">

**Author:** ![Nokel81](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/nokel81/32/3966_2.png) [@Nokel81](https://internals.rust-lang.org/u/Nokel81)\
**Post date:** [January 18, 2022, 8:38pm UTC](https://internals.rust-lang.org/t/a-read-macro-for-std-io/15950/9 "2022-01-18T20:38:48Z")

</div>

Interesting I would have thought it would limit of to a number of graphemes bit it doesn't.

---

<div class="post-metadata">

**Author:** ![Aloso](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/aloso/32/5039_2.png) [@Aloso](https://internals.rust-lang.org/u/Aloso)\
**Post date:** [January 18, 2022, 11:30pm UTC](https://internals.rust-lang.org/t/a-read-macro-for-std-io/15950/10 "2022-01-18T23:30:52Z")

</div>

Yeah, the standard library has only limited Unicode support. It supports text encoding, some character properties and case conversion, but anything beyond that (normalisation, segmentation, collation, ...) requires additional crates.

---

<div class="post-metadata">

**Author:** ![hyeonu](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/hyeonu/32/2162_2.png) [@hyeonu](https://internals.rust-lang.org/u/hyeonu)\
**Post date:** [January 19, 2022, 12:53am UTC](https://internals.rust-lang.org/t/a-read-macro-for-std-io/15950/11 "2022-01-19T00:53:47Z")

</div>

Grapheme based segmentation requires [unicode database](https://github.com/unicode-rs/unicode-segmentation/blob/master/src/tables.rs) which might be too big to be embedded in every binaries depend on libstd.

---

<div class="post-metadata">

**Author:** ![Nokel81](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/nokel81/32/3966_2.png) [@Nokel81](https://internals.rust-lang.org/u/Nokel81)\
**Post date:** [January 19, 2022, 3:17am UTC](https://internals.rust-lang.org/t/a-read-macro-for-std-io/15950/12 "2022-01-19T03:17:39Z")

</div>

Ah fair enough.

---

<div class="post-metadata">

**Author:** ![T-O-R-U-S](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/t-o-r-u-s/32/8156_2.png) [@T-O-R-U-S](https://internals.rust-lang.org/u/T-O-R-U-S)\
**Post date:** [January 21, 2022, 9:31am UTC](https://internals.rust-lang.org/t/a-read-macro-for-std-io/15950/13 "2022-01-21T09:31:54Z")

</div>

Good input, but this kind of defeats the entire purpose of not being ugly... I think that might actually be even worse!

---

<div class="post-metadata">

**Author:** ![Aloso](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/aloso/32/5039_2.png) [@Aloso](https://internals.rust-lang.org/u/Aloso)\
**Post date:** [January 21, 2022, 6:56pm UTC](https://internals.rust-lang.org/t/a-read-macro-for-std-io/15950/14 "2022-01-21T18:56:25Z")

</div>

> [@T-O-R-U-S](#):
>
> this kind of defeats the entire purpose of not being ugly

It's verbose (I wouldn't say ugly) because this is a very uncommon operation. I don't remember ever reading code that does this, and I honestly don't understand why you want to do this. If you just read a line without enforcing a maximum string length, the code becomes much easier.

And if it's not beautiful enough for you, you can always write a helper function or a macro yourself.

---

<div class="post-metadata">

**Author:** ![system](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/system/32/14092_2.png) [@system](https://internals.rust-lang.org/u/system)\
**Post date:** [April 21, 2022, 6:56pm UTC](https://internals.rust-lang.org/t/a-read-macro-for-std-io/15950/15 "2022-04-21T18:56:53Z")

</div>

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.
