I want to do this:
R, _ := strconv.Atoi(reader.ReadString(" ")) // reader = bufio.NewReader()
and the problem is, that strconv.Atoi expects one argument, but reader.ReadString() returns not only the string, but also the "err". Is there a way to solve this without creating variables or generally on just one line?
In Go, write a readInt
function to hide the complexity and always check for errors. Other answers have bugs: Howl (drops last number) and Raduan Santos (doesn't compile).
For example,
package main
import (
"bufio"
"fmt"
"io"
"log"
"strconv"
"strings"
)
func readInt(r *bufio.Reader) (int, error) {
const delim = ' '
str, err := r.ReadString(delim)
if err != nil && len(str) == 0 {
return 0, err
}
str = strings.TrimSuffix(str, string(delim))
return strconv.Atoi(str)
}
func main() {
input := "93 31 11 11"
fmt.Println(input)
rdr := bufio.NewReader(strings.NewReader(input))
for {
n, err := readInt(rdr)
if err != nil {
if err == io.EOF {
break
}
log.Fatal(err)
}
fmt.Println(n)
}
}
Output:
93 31 11 11
93
31
11
11
The idiomatic way to do it in Go is to handle the error gracefully with the usual if err != nil
. Use it.
reader := bufio.NewReader(strings.NewReader("93 31 11 11"))
numRaw, err := reader.ReadString(' ')
if err != nil {
log.Fatal(err)
}
// We need to substring numRaw because it contains also the trailing space (this is how ReadString works in bufio.Reader)
num, err := strconv.Atoi(numRaw[:len(numRaw)-1])
if err != nil {
log.Fatal(err)
}
log.Print(num)
If you want to make the code "cleaner", split it into functions.
func main() {
reader := bufio.NewReader(strings.NewReader("93 31 11 11"))
n, err := readInt(reader)
if err != nil {
log.Fatal(err)
}
log.Print(n)
}
func readInt(r *bufio.Reader) (int, error) {
numRaw, err := r.ReadString(' ')
if err != nil {
return 0, err
}
return strconv.Atoi(numRaw[:len(numRaw)-1])
}
For sake of completeness, there are some cases where it is useful to ignore the error because you know there won't be any. Such cases include parser which are always given the same input, ie. regexes and templates. These provide the useful idiom "Must": for instance, regexp.MustCompile
is a very simple wrapper around regexp.Compile
, which checks the error and panics if it's not nil.
func MustCompile(str string) *Regexp {
regexp, error := Compile(str)
if error != nil {
panic(`regexp: Compile(` + quote(str) + `): ` + error.Error())
}
return regexp
}
This is most often seen in Go programs when there is a regex which is used as a singleton and is declared in the global scope.
var usernameRegex = regexp.MustCompile("^[0-9A-Za-z-]+$")
func main() {
fmt.Println(usernameRegex.MatchString("Hello"))
fmt.Println(usernameRegex.MatchString(""))
}
To help think about when it is useful to use or add a "Must" function, I think of it as "If there was a way in Go to add a compile-time check, this would be a good place to use it".
As I said, this is also provided in the template packages of the Go standard library, such as html/template
. These don't provide a Must- function for everything, but they have a simple template.Must
function (which is closer to what you were trying to do in the OP). This way you can "chain" functions which would normally return a template and an error, so that they only return the template (and panic if there is any error).
var t = template.Must(template.New("hi").Parse(`{{ if .Bool }}Hello{{ else }}Goodbye{{ end }} world!`))
func main() {
t.ExecuteTemplate(os.Stdout, "hi", struct{Bool bool}{Bool: false})
}
This works, because template.Must
has a signature func(*Template, error)
, and Parse
returns (*Template, error)
, so return values and function arguments match. For instance, your example could have worked if strconv.Atoi
had the signature func(string, error)
.