No public stdout? (_Stdout is internal)

I wanted to create a generic struct which should generate output on any TextOutputStream and started with something like that:
struct OutputManager<Target: TextOutputStream>(...) {...}

Then I wanted to try it with some output on the "normal standard output" only to find that there is no "normal standard output", at least not publicly available.

There is an internal _Stdout in the standard library which seems to be exactly what I need, but I can't use it.

Am I missing something else?
Why is _Stdout internal?
Coming from C++ I may still have a different mindset which is not always helpful for doing it the "Swift way".

BTW: It is extremely difficult to find useful information on TextOutputStream. Searching the still current "The Swift Programming Language (Swift 5.7)" book reveals zero hits.

Dirk

2 Likes

Last I had to face this situation myself, the 'state of the art' was still to manually create a TextOutputStream implementation that just calls print.

I too don't see any valid reason that there's not a stdlib-provided standardOutput global. It seems like just an oversight in the API.

1 Like

The C symbols for stdout and stderr can be accessed by importing Foundation or, if you don't want the Foundation dependency, the platform-specific C standard library, i.e. Darwin on Apple platforms or Glibc on Linux.

This works for me (tested on macOS and Ubuntu via Docker):

#if canImport(Darwin)
import Darwin
#else
import Glibc
#endif

struct StandardOut: TextOutputStream {
    func write(_ string: String) {
        guard !string.isEmpty else { return }
        fputs(string, stdout)
    }
}

struct StandardError: TextOutputStream {
    func write(_ string: String) {
        guard !string.isEmpty else { return }
        fputs(string, stderr)
    }
}

var standardOut = StandardOut()
var standardError = StandardError()
print("This goes to stdout", to: &standardOut)
print("This goes to stderr", to: &standardError)
1 Like

You can use this:

public struct StandardOutput: TextOutputStream {
  public mutating func write(_ string: String) {
    print(string, terminator: "")
  }
  public init() {}
}

I don't know why it isn't available publicly, but it might be because:

  • The authors of the standard library didn't know what to call it. "stdout" is a term of art, and its meaning isn't immediately clear unless you have experience with programming.
  • They didn't think it belongs in the standard library since the type isn't necessary for a lot of programs.
  • You can already use stdout from the platform's C standard library (Darwin on Mac, Glibc on most Linux systems).
  • The type was overlooked when the standard library was created.

Just FYI: the purpose of the Swift book is to be an "authoritative reference for Swift, offering a guided tour, a comprehensive guide, and a formal reference of the language." While it describes some of Swift's most essential types, it is not intended to thoroughly document the standard library. For that, we have the standard library's documentation.

The documentation for TextOutputStream is here. It should be noted, though, that the TextOutputStream protocol isn't used very often in my experience.

Also, the latest version of the Swift book as of the time of writing is "The Swift Programming Language (5.10 beta)", available here.

1 Like

Thank all of you for the answers. I'm still a little bit concerned that the standard library uses some kind of internal locking which the print- and fputs- solution lack. But IIRC fputs should be thread-safe and print will use _Stdout internally, i.e., both solutions should be safe without additional locking.

I think streams should be used more often, especially if the alternative is CustomStringConvertible, which at least once was a performance bottleneck for me.

Thank you, I didn't know that and was still using the Apple Books version.

2 Likes