What is the IO interface?

@jameson recently mentioned an IO interface in a review. The lack of documentation on this matter was mentioned several years ago.

My best guess at that moment is that the core interface is inferrable from this part of boot.jl focusing on output:

High Level Required

  • write(io::IO, x::UInt8)::Int - Default implementation via Base.io_pointer(io::IO)
  • write(io::IO, x::String)::Int - Default implementation via Base.unsafe_write(io::IO, Ptr{UInt8}, nb::Int).
  • show(io::IO, @nospecialize x)::Nothing
  • print(io::IO, x::AbstractChar)::Nothing

Low-level interface

  • Core.io_pointer(io::IO)::Ptr{Cvoid}
  • unsafe_write(io::IO, x::Ptr{UInt8}, nb::UInt)::Int - Default implementation via Base.io_pointer(io::IO)
  • unsafe_write(io::IO, x::Ptr{UInt8}, nb::Int):;Int - Default implementation via Base.io_pointer(io::IO)


  • print(io::IO, x::String) - Default implementation via write(io::IO, x::String)
  • print(io::IO, x) - Default implementation via show(io::IO, x)
  • print(io::IO, x, a...) - Default implementation via print(io::IO, x)
  • println(io::IO) - Default implementation via write(io::IO, x::UInt8)
  • println(io::IO, x...) - Default implementation via print(io::IO, x, a...) and println(io::IO)

Other functions

  • lock(io::IO)
  • unmark(io::IO)
  • unlock(io::IO)
  • reseteof(io::IO)
  • buffer_writes(x::IO, bufsize=Base.SZ_UNBUFFERED_IO)
  • isopen(io::IO)
  • close(io::IO)
  • closewrite(io::IO)
  • flush(io::IO)
  • bytesavailable(io::IO)
  • readavailable(io::IO)
  • eof(io::IO)
  • copy(io::IO)
  • wait_readnb
  • wait_close
  • read(io::IO, String)
  • unsafe_read(s::IO, p::Ptr{UInt8}, n::UInt64)
  • unsafe_read(s::IO, p::Ptr, n::Integer)
  • unsafe_read(s::IO, p::Ref{T}, n::Integer)

I’m getting lost in this.

My sense is that we are missing several abstract subtypes of IO or IO traits.


IIRC, when you’re not in bootstrap mode, then show is implemented via print and print of Char is implemented in terms of write.

io_pointer is an internal thing that’s used for IO types that correspond to libuv I/O streams. I don’t think other IO types should implement it as long as they implement their own write(io, byte::UInt8) and unsafe_write(io, bytes::Ptr{UInt8}, num_bytes::UInt) methods.

(I suspect that boot.jl is not always the best guide to this sort of thing, because bootstrapping is kind of a weird environment.)


Thanks for looking into this. The docs definitely could improve.

In a similar vein I started collecting gotchas/tips on IO with a view to expanding the docs, but it never matured enough to start a PR. Just in case it’s helpful: https://hackmd.io/nlqvf7cjT6qcfW76cHLHqw