8000 readline() function to ask for user input · Issue #867 · inko-lang/inko · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

readline() function to ask for user input #867

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

Open
PLC-Programmer opened this issue May 18, 2025 · 5 comments
Open

readline() function to ask for user input #867

PLC-Programmer opened this issue May 18, 2025 · 5 comments
Labels
accepting contributions Issues that are suitable to be worked on by anybody, not just maintainers feature New things to add to Inko, such as a new standard library module std Changes related to the standard library

Comments

@PLC-Programmer
Copy link

Description

Hi,
I have a feature request: a readline() function would be helpful (for me) for reading into a ByteArray until a newline is entered by the user:

import std.stdio (Stdin)
let stdin = Stdin.new
let bytes = ByteArray.new
stdin.readline(bytes)

See with Scala for example: String = scala.io.StdIn.readLine() --> "Ask For User Input": https://docs.scala-lang.org/scala3/book/taste-hello-world.html#ask-for-user-input

or in Rust for example: std::io::stdin().read_line(&mut String) --> https://doc.rust-lang.org/std/io/struct.Stdin.html#method.read_line

I also like the Swift implementation, where I can set a flag to get rid of the final newline character or not:

func readLine(strippingNewline: Bool = true) -> String? --> https://developer.apple.com/documentation/swift/readline(strippingnewline:)

Related work

No response

@PLC-Programmer PLC-Programmer added the feature New things to add to Inko, such as a new standard library module label May 18, 2025
@yorickpeterse
Copy link
Collaborator

BufferedReader.read_line (offered through an implementation of BufferedRead.read_line) allows reading of individual lines, and supports both including or excluding the trailing newline.

An issue is that this method tries to fill its internal buffer up first, which only works reliably if the input stream actually has an end. With STDIN that isn't the case unless the parent process closes STDIN. To work around this, you have to set BufferedReader.exact to false:

import std.io (BufferedReader)
import std.stdio (Stdin, Stdout)

type async Main {
  fn async main {
    let out = Stdout.new
    let inp = BufferedReader.new(Stdin.new)
    let buf = ByteArray.new

    inp.exact = false

    let _ = inp.read_line(into: buf, inclusive: false).or_panic
    let _ = out.print(buf.into_string)
  }
}

This then behaves as you'd expect: it reads until the input until the newline and in this case (due to inclusive: false) doesn't include it in the target buffer.

Thinking about it, the use of read_exact when filling up the internal buffer is probably a mistake. On most platforms the amount of bytes you can read in a single read is far greater than the buffer size. We also mask signals outside of the signal handler thread, so a read is unlikely to be interrupted in many cases.

In short, what we should do is this:

  1. Remove BufferedReader.exact
  2. Change BufferedReader.fill_buffer to perform a single regular read instead of using read_exact
  3. Update the documentation of BufferedRead.fill_buffer to clarify that it's allowed to fill the buffer with fewer bytes than the desired buffer size (i.e. the buffer size is a maximum, not the exact desired amount)

@yorickpeterse yorickpeterse added accepting contributions Issues that are suitable to be worked on by anybody, not just maintainers std Changes related to the standard library labels May 18, 2025
@PLC-Programmer
Copy link
Author

This solution didn't work for me.
Command inp.exact = false led to an error message while compiling: ...the method 'exact=' isn't defined for type 'BufferedReader[Stdin]

However, I managed with using the read() function like this -- which is not a safe solution, but kept my work flow going forward:

import std.stdio (Stdin, Stdout)
type async Main {
  fn async main {
    let stdin = Stdin.new
    let out = Stdout.new
    let buf = ByteArray.new
    out.write_string("\nMake an input and then press the ENTER key: ")

    stdin.read(buf, 10000)  # this is a non-safe workaround

    out.print("Your input was: ${buf.to_string}")
  }
}

@yorickpeterse
Copy link
Collaborator

@PLC-Programmer The supplied snippet relies on changes not yet released, sorry for not making that more clear.

Your snippet itself isn't sufficient: all read(buf, 10000) does is read up to 10000 bytes into buf. Unless you're manually splitting that data into separate lines, it won't be sufficient.

If possible I recommend using the main branch instead.

@PLC-Programmer
Copy link
Author

This solution works like a charm as a work around:

$./user_input_console_test

Make an input and then press the ENTER key: dsd43.#
Your input was: dsd43.
#

$

But it's not safe. When the user gets the idea - and depending on a program different from the shown above - to enter more than 10,000 "characters", dangerous things can happen.

@yorickpeterse
Copy link
Collaborator
yorickpeterse comm 67A8 ented May 19, 2025

@PLC-Programmer It largely works by coincidence due to how the Enter key is handled in interactive terminal sessions, but this won't be the case when e.g. piping input to STDIN. For example, if you pipe "foo\nbar\nbaz" to the program it will read all of that into the ByteArray, instead of only including foo.

Either way, for the purpose of just playing around your hack might be sufficient, though I still recommend using main in general 😃

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
accepting contributions Issues that are suitable to be worked on by anybody, not just maintainers feature New things to add to Inko, such as a new standard library module std Changes related to the standard library
Projects
None yet
Development

No branches or pull requests

2 participants
0