If you want a golang reader to string flow, you are draining something that implements io.Reader until EOF, then turning the bytes into a string. The same pattern answers golang io.reader to string and io.readcloser to string / golang io.readcloser to string, because io.ReadCloser is still an io.Reader for the Read side. For a golang io reader example, the standard library gives io.ReadAll, io.Copy into strings.Builder, or bytes.Buffer.ReadFrom. People who search for strings.newreader usually need the opposite direction—a golang reader from string—which is strings.NewReader, not a reader-to-string conversion.
Tested with Go 1.24 on Linux.
From io.Reader or io.ReadCloser to string
io.ReadAll then string(bytes) (most direct)
io.ReadAll reads until EOF and returns a []byte and an error. On success, string(b) gives your string. This is the default choice for golang io.reader to string when you want the entire stream.
Run it: you should see This is GoLinuxCloud! printed.
package main
import (
"fmt"
"io"
"log"
"strings"
)
func main() {
r := strings.NewReader("This is GoLinuxCloud!")
b, err := io.ReadAll(r)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(b))
}strings.Builder and io.Copy
strings.Builder implements io.Writer. io.Copy streams from the reader into the builder until EOF, then String() returns the result. Useful when you already pipeline through io.Copy or combine multiple writes.
Run it: same line of output as the ReadAll example.
package main
import (
"fmt"
"io"
"log"
"strings"
)
func main() {
r := strings.NewReader("This is GoLinuxCloud!")
var buf strings.Builder
if _, err := io.Copy(&buf, r); err != nil {
log.Fatal(err)
}
fmt.Println(buf.String())
}bytes.Buffer and ReadFrom
bytes.Buffer is a growable byte buffer with ReadFrom and String. It is a good fit when you want []byte access as well as string, or when older code already centers on bytes.Buffer.
Run it: same final line as above.
package main
import (
"bytes"
"fmt"
"log"
"strings"
)
func main() {
r := strings.NewReader("This is GoLinuxCloud!")
var buf bytes.Buffer
if _, err := buf.ReadFrom(r); err != nil {
log.Fatal(err)
}
fmt.Println(buf.String())
}io.ReadCloser and Close
For types like HTTP response bodies, the concrete type is often an io.ReadCloser. Reading until EOF with io.ReadAll or io.Copy is unchanged. Call Close() when the documentation says it frees a socket or file descriptor; it does not change how you build the string after the reads succeed.
Reader from a string: strings.NewReader
strings.NewReader returns an io.Reader (and more) that reads from an existing string. That matches golang string reader and golang reader from string intent. It does not allocate a []byte copy of the whole string up front the way some older patterns did; it is read-only and efficient for passing string data into APIs that want a reader.
package main
import (
"fmt"
"io"
"log"
"strings"
)
func main() {
r := strings.NewReader("peek")
buf := make([]byte, 2)
_, err := r.Read(buf)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(buf))
}You should see pe (the first two bytes).
For turning a byte slice into a reader, see byte slice to io.Reader. For the reverse of this article—reader to raw []byte—io.ReadAll already returns []byte; you can also use a buffer as in that post.
io.Writer, io.ReadWriter, and go io reader writer
Queries such as go io reader writer usually point at io.ReadWriter, the interface that combines io.Reader and io.Writer. Types like net.Conn implement both ends. Converting a reader to a string does not require io.Writer unless you choose the io.Copy(dst, src) form, where dst must be a Writer—strings.Builder and bytes.Buffer satisfy that.
Summary
For golang reader to string, read the stream to completion with io.ReadAll and convert with string(b), or stream into strings.Builder via io.Copy, or use bytes.Buffer.ReadFrom then String(). io.ReadCloser uses the same read path; Close is about resources, not string assembly. strings.NewReader is the strings.newreader / golang io.reader from string direction: it wraps a string as a Reader, not the other way around. Prefer io.ReadAll over deprecated ioutil.ReadAll on supported Go versions.

