Module net/addr
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
An IP address, either IPv4 or IPv6.
Variants
| Variant | Fields | Description |
|---|---|---|
V4 | a: u8, b: u8, c: u8, d: u8 | An IPv4 address represented as four octets. |
V6 | segments: 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) -> StringRender self under spec. An unrecognised spec degrades to the plain
to_string() rendering rather than failing.
Parameters
| Name | Type | Notes |
|---|---|---|
self | Self | |
spec | str |
Returns: String
impl(IpAddr, ...)
parse_v4 : (IpAddr) fn(s : String, exn : Exception) -> IpAddrParse 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
| Name | Type | Notes |
|---|---|---|
s | String | |
exn | Exception |
Returns: IpAddr
parse_v6 : (IpAddr) fn(s : String, exn : Exception) -> IpAddrParse 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
| Name | Type | Notes |
|---|---|---|
s | String | |
exn | Exception |
Returns: IpAddr
parse : (IpAddr) fn(s : String, exn : Exception) -> IpAddrParse 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
| Name | Type | Notes |
|---|---|---|
s | String | |
exn | Exception |
Returns: IpAddr
loopback_v4 : (IpAddr) fn() -> IpAddrReturn the IPv4 loopback address (127.0.0.1).
Returns: IpAddr
loopback_v6 : (IpAddr) fn() -> IpAddrReturn the IPv6 loopback address (::1).
Returns: IpAddr
any_v4 : (IpAddr) fn() -> IpAddrReturn the IPv4 unspecified address (0.0.0.0).
Returns: IpAddr
is_loopback : (IpAddr) fn(self : IpAddr) -> boolis_v4 : (IpAddr) fn(self : IpAddr) -> boolis_v6 : (IpAddr) fn(self : IpAddr) -> boolany_v6 : (IpAddr) fn() -> IpAddrThe 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)segments : (IpAddr) fn(self : IpAddr) -> Option(Array(u16, 8))is_unspecified : (IpAddr) fn(self : IpAddr) -> bool0.0.0.0 or :: — the "any address" of either family.
Parameters
| Name | Type | Notes |
|---|---|---|
self | IpAddr |
Returns: bool
is_multicast : (IpAddr) fn(self : IpAddr) -> boolA multicast address — 224.0.0.0/4 (RFC 5771) or ff00::/8
(RFC 4291 §2.7).
Parameters
| Name | Type | Notes |
|---|---|---|
self | IpAddr |
Returns: bool
is_private : (IpAddr) fn(self : IpAddr) -> boolAn 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
| Name | Type | Notes |
|---|---|---|
self | IpAddr |
Returns: bool
is_unique_local : (IpAddr) fn(self : IpAddr) -> boolA 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
| Name | Type | Notes |
|---|---|---|
self | IpAddr |
Returns: bool
is_link_local : (IpAddr) fn(self : IpAddr) -> boolA link-local address — 169.254/16 (RFC 3927) or fe80::/10
(RFC 4291 §2.5.6).
Parameters
| Name | Type | Notes |
|---|---|---|
self | IpAddr |
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, ...)
impl(IpAddr, Eq(IpAddr)(...))
impl(IpAddr, Ord(IpAddr)(...))
impl(IpAddr, Hash(...))
hash : (IpAddr) fn(generic(H) self : IpAddr, hasher : H : (Hasher)) -> unitimpl(IpAddr, Clone(...))
Methods
to_string : (IpAddr) fn(self : IpAddr) -> String== : (IpAddr) fn(lhs : IpAddr, rhs : IpAddr) -> bool!= : (IpAddr) fn(lhs : IpAddr, rhs : IpAddr) -> bool< : (IpAddr) fn(lhs : IpAddr, rhs : IpAddr) -> bool<= : (IpAddr) fn(lhs : IpAddr, rhs : IpAddr) -> bool> : (IpAddr) fn(lhs : IpAddr, rhs : IpAddr) -> boolAn 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
| Name | Type | Description |
|---|---|---|
ip | IpAddr | The IP address. |
port | u16 | 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) -> StringRender self under spec. An unrecognised spec degrades to the plain
to_string() rendering rather than failing.
Parameters
| Name | Type | Notes |
|---|---|---|
self | Self | |
spec | str |
Returns: String
impl(SocketAddr, ...)
new : (SocketAddr) fn(ip : IpAddr, port : u16) -> SocketAddrCreate a new socket address from an IP and port.
Parameters
| Name | Type | Notes | Description |
|---|---|---|---|
ip | IpAddr | The IP address. | |
port | u16 | The port number. |
Returns: SocketAddr
loopback : (SocketAddr) fn(port : u16) -> SocketAddrCreate an IPv4 loopback socket address (127.0.0.1) with the given port.
Parameters
| Name | Type | Notes | Description |
|---|---|---|---|
port | u16 | The port number. |
Returns: SocketAddr
any : (SocketAddr) fn(port : u16) -> SocketAddrCreate an IPv4 unspecified socket address (0.0.0.0) with the given port.
Parameters
| Name | Type | Notes | Description |
|---|---|---|---|
port | u16 | The port number. |
Returns: SocketAddr
parse : (SocketAddr) fn(s : String, exn : Exception) -> SocketAddrParse 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
| Name | Type | Notes |
|---|---|---|
s | String | |
exn | Exception |
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) -> OrderingCompare two addresses — .Less / .Equal / .Greater.
Parameters
| Name | Type | Notes |
|---|---|---|
lhs | SocketAddr | |
rhs | SocketAddr |
Returns: Ordering
impl(SocketAddr, Hash(...))
hash : (SocketAddr) fn(generic(H) self : SocketAddr, hasher : H : (Hasher)) -> unitFeeds 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
| Name | Type | Notes |
|---|---|---|
self | SocketAddr | |
hasher | H : (Hasher) |
Returns: unit
impl(SocketAddr, Clone(...))
clone : (SocketAddr) fn(self : SocketAddr) -> SocketAddrAn IpAddr is a plain value — four octets or eight segments, no heap
— so cloning is a copy.
Parameters
| Name | Type | Notes |
|---|---|---|
self | SocketAddr |
Returns: SocketAddr
Methods
to_string : (SocketAddr) fn(self : SocketAddr) -> StringRender 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
| Name | Type | Notes |
|---|---|---|
self | SocketAddr |
Returns: String
== : (SocketAddr) fn(lhs : SocketAddr, rhs : SocketAddr) -> bool!= : (SocketAddr) fn(lhs : SocketAddr, rhs : SocketAddr) -> bool< : (SocketAddr) fn(lhs : SocketAddr, rhs : SocketAddr) -> bool<= : (SocketAddr) fn(lhs : SocketAddr, rhs : SocketAddr) -> bool> : (SocketAddr) fn(lhs : SocketAddr, rhs : SocketAddr) -> bool>= : (SocketAddr) fn(lhs : SocketAddr, rhs : SocketAddr) -> bool