Module net/addr

net/addr
Stability: unstable — the PARSE half violates a decided convention and is expected to change shape. `IpAddr.parse` / `parse_v4` / `parse_v6` and `SocketAddr.parse` are pure text decoders that report failure by THROWING through `Exception`, with the reason in a `NetError.Other(String)` message; D13 makes `Result` the primary spelling for a pure decoder (that is what `Url.parse` was flipped to) and D1 rules out stringly-typed errors, so the expected end state is `Result(_, AddrParseError)` — Rust's `AddrParseError` — with the `*_exn` wrapper on the side. The address TYPES themselves (variants, constructors, `to_string`, the four trait impls) match Rust and are not expected to move; freezing waits on the parse-error decision. — stable modules only change additively; this one may still change.

Network address types — IpAddr (v4 / v6) and SocketAddr (an address plus a port), the values every call in std/net speaks.

Both are VALUE types with Eq / Ord / Hash / Clone, so an address is usable as a HashMap key (a connection table) and sortable (a peer list); ordering is the structural one Rust derives — for SocketAddr, IP first and port second. Nothing here does I/O: parsing and rendering are pure, and name resolution lives in std/net/dns.

{ IpAddr, SocketAddr } :: import("std/net/addr");

addr := SocketAddr.new(IpAddr.loopback_v4(), u16(8080));
peer := SocketAddr.parse(`[::1]:80`, exn);   // throws on bad input

Stability

unstable — the PARSE half violates a decided convention and is expected to change shape. IpAddr.parse / parse_v4 / parse_v6 and SocketAddr.parse are pure text decoders that report failure by THROWING through Exception, with the reason in a NetError.Other(String) message; D13 makes Result the primary spelling for a pure decoder (that is what Url.parse was flipped to) and D1 rules out stringly-typed errors, so the expected end state is Result(_, AddrParseError) — Rust's AddrParseError — with the *_exn wrapper on the side. The address TYPES themselves (variants, constructors, to_string, the four trait impls) match Rust and are not expected to move; freezing waits on the parse-error decision.

Types

IpAddr enum
IpAddr

An IP address, either IPv4 or IPv6.

Variants

VariantFieldsDescription
V4a: u8, b: u8, c: u8, d: u8

An IPv4 address represented as four octets.

V6segments: Array(u16, 8)

An IPv6 address represented as eight 16-bit segments.

Trait Implementations

impl(generic(T : Type), where(T <: ToString), T : (ToString))
impl(generic(T : Type), where(T <: ToString), T : (ToString), Format)
format : fn(self : Self, spec : str) -> String

Render self under spec. An unrecognised spec degrades to the plain to_string() rendering rather than failing.

Parameters

NameTypeNotes
selfSelf
specstr

Returns: String

impl(IpAddr, ...)
parse_v4 : (IpAddr) fn(s : String, exn : Exception) -> IpAddr

Parse an IPv4 address from a dotted-decimal String (e.g. "127.0.0.1"). Throws on invalid input. Exactly four octets of 1–3 decimal digits, each 0–255, no leading zeros (01.2.3.4 is rejected, as Rust's Ipv4Addr::from_str does — octal ambiguity), no empty octet (1..2.3, 1.2.3., ... used to parse as 0-filled addresses).

Parameters

NameTypeNotes
sString
exnException

Returns: IpAddr

parse_v6 : (IpAddr) fn(s : String, exn : Exception) -> IpAddr

Parse an IPv6 address from its text form (RFC 4291 §2.2), throwing on invalid input.

Accepts all three RFC forms:

  • the full eight groups — 2001:db8:0:0:0:0:2:1;
  • :: compressing ONE run of zero groups — 2001:db8::2:1, ::1, ::;
  • a trailing dotted-quad for the last 32 bits — ::ffff:192.0.2.1, which is how an IPv4-mapped address is written.

Hex digits are case-insensitive on input. Rejected, each with its own message: more than one ::, more than eight groups, a group of more than four hex digits, an empty group that is not part of a ::, a single leading or trailing :, a non-hex byte, and a dotted-quad anywhere but at the end. A zone id (%eth0) is NOT accepted — it identifies an interface, not an address, and IpAddr has nowhere to put it.

Parameters

NameTypeNotes
sString
exnException

Returns: IpAddr

parse : (IpAddr) fn(s : String, exn : Exception) -> IpAddr

Parse host:port, throwing on invalid input.

A V6 host MUST be bracketed — [::1]:80 — because a bare ::1:80 is ambiguous: 80 could be the port or the last group. That is exactly why RFC 3986 §3.2.2 introduced the brackets, and why this rejects the unbracketed form rather than guessing.

Parameters

NameTypeNotes
sString
exnException

Returns: IpAddr

loopback_v4 : (IpAddr) fn() -> IpAddr

Return the IPv4 loopback address (127.0.0.1).

Returns: IpAddr

loopback_v6 : (IpAddr) fn() -> IpAddr

Return the IPv6 loopback address (::1).

Returns: IpAddr

any_v4 : (IpAddr) fn() -> IpAddr

Return the IPv4 unspecified address (0.0.0.0).

Returns: IpAddr

is_loopback : (IpAddr) fn(self : IpAddr) -> bool

Return true if this is a loopback address.

Parameters

NameTypeNotes
selfIpAddr

Returns: bool

is_v4 : (IpAddr) fn(self : IpAddr) -> bool

Return true if this is an IPv4 address.

Parameters

NameTypeNotes
selfIpAddr

Returns: bool

is_v6 : (IpAddr) fn(self : IpAddr) -> bool

Return true if this is an IPv6 address.

Parameters

NameTypeNotes
selfIpAddr

Returns: bool

any_v6 : (IpAddr) fn() -> IpAddr

The IPv6 unspecified address (::) — the counterpart of any_v4, and what a dual-stack listener binds to.

Returns: IpAddr

octets : (IpAddr) fn(self : IpAddr) -> ArrayList(u8)

The four octets of a V4 address, or the V6 address's sixteen bytes packed big-endian. One accessor for either family, because a caller serializing an address wants the bytes whatever it is.

Parameters

NameTypeNotes
selfIpAddr

Returns: ArrayList(u8)

segments : (IpAddr) fn(self : IpAddr) -> Option(Array(u16, 8))

The eight 16-bit groups of a V6 address. .None for a V4 address: a V4 address has no groups, and returning the IPv4-mapped ones would silently answer a different question.

Parameters

NameTypeNotes
selfIpAddr

Returns: Option(Array(u16, 8))

is_unspecified : (IpAddr) fn(self : IpAddr) -> bool

0.0.0.0 or :: — the "any address" of either family.

Parameters

NameTypeNotes
selfIpAddr

Returns: bool

is_multicast : (IpAddr) fn(self : IpAddr) -> bool

A multicast address — 224.0.0.0/4 (RFC 5771) or ff00::/8 (RFC 4291 §2.7).

Parameters

NameTypeNotes
selfIpAddr

Returns: bool

is_private : (IpAddr) fn(self : IpAddr) -> bool

An address in one of the RFC 1918 private ranges — 10/8, 172.16/12, 192.168/16.

V6 has no RFC 1918; its nearest equivalent is the fc00::/7 unique-local range, which is_unique_local reports separately rather than being folded in here — the two have different routing rules and conflating them is how an access check ends up wrong.

Parameters

NameTypeNotes
selfIpAddr

Returns: bool

is_unique_local : (IpAddr) fn(self : IpAddr) -> bool

A V6 unique-local address (fc00::/7, RFC 4193) — the V6 analogue of RFC 1918, kept separate from is_private on purpose. Always false for V4.

Parameters

NameTypeNotes
selfIpAddr

Returns: bool

impl(IpAddr, ToString(...))
to_string : ( self -> { return( match( self, .V4(a, b, c, d) => { buf := Array(u8, usize(16)).fill(u8(0)); unsafe(snprintf((*char)(&buf(usize(0))), usize(16), "%d.%d.%d.%d", i32(a), i32(b), i32(c), i32(d))); String.from_cstr(&buf(usize(0))).unwrap() }, .V6(segs) => { w := StringBuilder.new(); i := usize(0); while(runtime(i < usize(8)), { cond( (i > usize(0)) => { w.write_str(":"); }, true => () ); w.write_hex(u64(segs(i))); i = (i + usize(1)); }); w.to_string() } ) ); } )
impl(IpAddr, ...)
cmp : (IpAddr) fn(lhs : IpAddr, rhs : IpAddr) -> Ordering

Compare two addresses — .Less / .Equal / .Greater.

Parameters

NameTypeNotes
lhsIpAddr
rhsIpAddr

Returns: Ordering

impl(IpAddr, Eq(IpAddr)(...))
impl(IpAddr, Ord(IpAddr)(...))
cmp : (IpAddr) fn(lhs : IpAddr, rhs : IpAddr) -> Ordering

Compare two addresses — .Less / .Equal / .Greater.

Parameters

NameTypeNotes
lhsIpAddr
rhsIpAddr

Returns: Ordering

impl(IpAddr, Hash(...))
hash : (IpAddr) fn(generic(H) self : IpAddr, hasher : H : (Hasher)) -> unit

Feeds a one-byte family tag and then the address bytes, so a V4 address and a V6 address that happen to share a byte pattern do not collide by construction.

Parameters

NameTypeNotes
selfIpAddr
hasherH : (Hasher)

Returns: unit

impl(IpAddr, Clone(...))
clone : (IpAddr) fn(self : IpAddr) -> IpAddr

An IpAddr is a plain value — four octets or eight segments, no heap — so cloning is a copy.

Parameters

NameTypeNotes
selfIpAddr

Returns: IpAddr

Methods
to_string : (IpAddr) fn(self : IpAddr) -> String

Render self as the text a USER should read — Rust's Display::fmt, not its Debug. Hand-written (or generated by derive(Error) from a per-variant format string) whenever the structural form would be wrong.

Parameters

NameTypeNotes
selfIpAddr

Returns: String

== : (IpAddr) fn(lhs : IpAddr, rhs : IpAddr) -> bool

Parameters

NameTypeNotes
lhsIpAddr
rhsIpAddr

Returns: bool

!= : (IpAddr) fn(lhs : IpAddr, rhs : IpAddr) -> bool

Parameters

NameTypeNotes
lhsIpAddr
rhsIpAddr

Returns: bool

< : (IpAddr) fn(lhs : IpAddr, rhs : IpAddr) -> bool

Parameters

NameTypeNotes
lhsIpAddr
rhsIpAddr

Returns: bool

<= : (IpAddr) fn(lhs : IpAddr, rhs : IpAddr) -> bool

Parameters

NameTypeNotes
lhsIpAddr
rhsIpAddr

Returns: bool

> : (IpAddr) fn(lhs : IpAddr, rhs : IpAddr) -> bool

Parameters

NameTypeNotes
lhsIpAddr
rhsIpAddr

Returns: bool

>= : (IpAddr) fn(lhs : IpAddr, rhs : IpAddr) -> bool

Parameters

NameTypeNotes
lhsIpAddr
rhsIpAddr

Returns: bool

SocketAddr struct
SocketAddr

An IP address paired with a port number — Rust's SocketAddr, except that it is ONE type rather than Rust's SocketAddrV4/SocketAddrV6 pair, because the family already lives in the IpAddr it holds. A value type: copying it copies the address, and to_string brackets a v6 host ([::1]:80) so the result re-parses.

Fields

NameTypeDescription
ipIpAddr

The IP address.

portu16

The port number.

Trait Implementations

impl(generic(T : Type), where(T <: ToString), T : (ToString))
impl(generic(T : Type), where(T <: ToString), T : (ToString), Format)
format : fn(self : Self, spec : str) -> String

Render self under spec. An unrecognised spec degrades to the plain to_string() rendering rather than failing.

Parameters

NameTypeNotes
selfSelf
specstr

Returns: String

impl(SocketAddr, ...)
new : (SocketAddr) fn(ip : IpAddr, port : u16) -> SocketAddr

Create a new socket address from an IP and port.

Parameters

NameTypeNotesDescription
ipIpAddr

The IP address.

portu16

The port number.

Returns: SocketAddr

loopback : (SocketAddr) fn(port : u16) -> SocketAddr

Create an IPv4 loopback socket address (127.0.0.1) with the given port.

Parameters

NameTypeNotesDescription
portu16

The port number.

Returns: SocketAddr

any : (SocketAddr) fn(port : u16) -> SocketAddr

Create an IPv4 unspecified socket address (0.0.0.0) with the given port.

Parameters

NameTypeNotesDescription
portu16

The port number.

Returns: SocketAddr

parse : (SocketAddr) fn(s : String, exn : Exception) -> SocketAddr

Parse host:port, throwing on invalid input.

A V6 host MUST be bracketed — [::1]:80 — because a bare ::1:80 is ambiguous: 80 could be the port or the last group. That is exactly why RFC 3986 §3.2.2 introduced the brackets, and why this rejects the unbracketed form rather than guessing.

Parameters

NameTypeNotes
sString
exnException

Returns: SocketAddr

impl(SocketAddr, ToString(...))
to_string : ( self -> { ip_str := self.ip.to_string(); buf := Array(u8, usize(8)).fill(u8(0)); unsafe(snprintf((*char)(&buf(usize(0))), usize(8), "%d", i32(self.port))); port_str := String.from_cstr(&buf(usize(0))).unwrap(); return( match( self.ip, .V4(_, _, _, _) => `${ip_str}:${port_str}`, .V6(_) => `[${ip_str}]:${port_str}` ) ); } )
impl(SocketAddr, Eq(SocketAddr)(...))
impl(SocketAddr, Ord(SocketAddr)(...))
cmp : (SocketAddr) fn(lhs : SocketAddr, rhs : SocketAddr) -> Ordering

Compare two addresses — .Less / .Equal / .Greater.

Parameters

NameTypeNotes
lhsSocketAddr
rhsSocketAddr

Returns: Ordering

impl(SocketAddr, Hash(...))
hash : (SocketAddr) fn(generic(H) self : SocketAddr, hasher : H : (Hasher)) -> unit

Feeds a one-byte family tag and then the address bytes, so a V4 address and a V6 address that happen to share a byte pattern do not collide by construction.

Parameters

NameTypeNotes
selfSocketAddr
hasherH : (Hasher)

Returns: unit

impl(SocketAddr, Clone(...))
clone : (SocketAddr) fn(self : SocketAddr) -> SocketAddr

An IpAddr is a plain value — four octets or eight segments, no heap — so cloning is a copy.

Parameters

NameTypeNotes
selfSocketAddr

Returns: SocketAddr

Methods
to_string : (SocketAddr) fn(self : SocketAddr) -> String

Render self as the text a USER should read — Rust's Display::fmt, not its Debug. Hand-written (or generated by derive(Error) from a per-variant format string) whenever the structural form would be wrong.

Parameters

NameTypeNotes
selfSocketAddr

Returns: String

== : (SocketAddr) fn(lhs : SocketAddr, rhs : SocketAddr) -> bool

Parameters

NameTypeNotes
lhsSocketAddr
rhsSocketAddr

Returns: bool

!= : (SocketAddr) fn(lhs : SocketAddr, rhs : SocketAddr) -> bool

Parameters

NameTypeNotes
lhsSocketAddr
rhsSocketAddr

Returns: bool

< : (SocketAddr) fn(lhs : SocketAddr, rhs : SocketAddr) -> bool

Parameters

NameTypeNotes
lhsSocketAddr
rhsSocketAddr

Returns: bool

<= : (SocketAddr) fn(lhs : SocketAddr, rhs : SocketAddr) -> bool

Parameters

NameTypeNotes
lhsSocketAddr
rhsSocketAddr

Returns: bool

> : (SocketAddr) fn(lhs : SocketAddr, rhs : SocketAddr) -> bool

Parameters

NameTypeNotes
lhsSocketAddr
rhsSocketAddr

Returns: bool

>= : (SocketAddr) fn(lhs : SocketAddr, rhs : SocketAddr) -> bool

Parameters

NameTypeNotes
lhsSocketAddr
rhsSocketAddr

Returns: bool