Module fs/watch
File-system change notifications (fs.watch, plans/archive/STD_API_AUDIT.md §7 P1).
A Watcher observes one path — a directory (entries created, removed,
renamed or written) or a single file — through the runtime's native
backend (inotify on Linux, kqueue on macOS, ReadDirectoryChangesW on
Windows; the std/sys/events layer). Events are delivered on the event
loop and queued inside the watcher; read them with the non-blocking
poll() or the awaitable next(io). next is the Stream trait's
method (std/async/stream.yo), so a watcher composes:
w.filter(…).map(…).take(n).
{ watch, WatchOptions, FsEventKind } :: import("std/fs/watch");
w := watch(Path.new(`./logs`), WatchOptions.defaults(), exn);
ev := io.await(w.next(io), io); // Option(FsEvent) — .None once closed
match(ev, .Some(e) => println(`${e.name} ${e.kind}`), .None => ());
w.close();
close() is idempotent and runs automatically when the last reference to
the watcher goes away, so a watcher that simply falls out of scope stops
its native watch rather than leaving the event loop calling back into
freed memory.
Event kinds follow the backend's (and libuv's) two-way split: Rename
covers create / delete / move, Change covers a write or metadata change.
name is the entry name relative to a watched directory (the file's own
name when a file is watched) on every platform — on macOS the runtime
diffs a snapshot of the directory and reports each changed entry; an
event about the directory itself (or a vanished root) carries an empty
name. Platforms coalesce differently, so a single write may surface as one
or several Changes — treat the stream as "something happened", not as
an exact journal.
Stability
unstable — shipped in v0.2.20 and still unstable at v0.2.24. This is a
deliberate extension of the one-release window, not drift: the Windows
backend is not delivered, and the shape of Windows change delivery
(ReadDirectoryChangesW batches renames as paired events) is the one
thing most likely to move Change. macOS and Linux are usable today.
Freezing follows Windows delivery, not a release count.
Types
What happened to the entry.
Variants
| Variant | Fields | Description |
|---|---|---|
Rename | Created, removed or moved (the backend's "rename" class). | |
Change | Contents or metadata changed. |
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(FsEventKind, ToString(...))
Methods
to_string : (FsEventKind) fn(self : FsEventKind) -> 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 | FsEventKind |
Returns: String
One notification.
Fields
| Name | Type | Description |
|---|---|---|
name | String | Entry name relative to the watched directory (or the watched file's own name); empty when the backend reports no name. |
kind | FsEventKind | Which of the backend's two classes this was. Platforms coalesce
differently, so a |
Watch settings.
Fields
| Name | Type | Description |
|---|---|---|
recursive | bool | Watch the whole tree below a directory. Native on macOS/Windows; on Linux inotify is per directory, so this is best-effort there. |
impl(WatchOptions, ...)
defaults : (WatchOptions) fn() -> WatchOptionsWatch only the named directory itself — recursive : false. That is
the conservative default because recursive is best-effort on Linux
(inotify is per directory), so a program written against the default
behaves the same on every platform.
Returns: WatchOptions
A live watch. Create with watch, drain with poll/next, and close
when done — closing stops the native watch and makes next resolve
.None for any waiter.
Fields
| Name | Type | Description |
|---|---|---|
_handle | FsEventHandle | |
_root | Path | |
_queue | ArrayList(FsEvent) | |
_head | usize | Index of the first unread event in |
_active | bool |
Trait Implementations
impl(generic(S : Type), where(S <: Stream), S : (Stream))
map : fn(generic(A, B, F) self : S : (Stream), f : F : (Fn(A) -> B)) -> StreamMap(S : (Stream), B, F : (Fn(A) -> B))filter : fn(generic(A, F) self : S : (Stream), f : F : (Fn(A) -> bool)) -> StreamFilter(S : (Stream), F : (Fn(A) -> bool))Parameters
| Name | Type | Notes |
|---|---|---|
self | S : (Stream) | |
f | F : (Fn(A) -> bool) |
Returns: StreamFilter(S : (Stream), F : (Fn(A) -> bool))
filter_map : fn(generic(A, B, F) self : S : (Stream), f : F : (Fn(A) -> Option(B))) -> StreamFilterMap(S : (Stream), B, F : (Fn(A) -> Option(B)))Parameters
| Name | Type | Notes |
|---|---|---|
self | S : (Stream) | |
f | F : (Fn(A) -> Option(B)) |
Returns: StreamFilterMap(S : (Stream), B, F : (Fn(A) -> Option(B)))
take : fn(generic(A) self : S : (Stream), n : usize) -> StreamTake(S : (Stream))skip : fn(generic(A) self : S : (Stream), n : usize) -> StreamSkip(S : (Stream))for_each : fn(generic(A, F) self : S : (Stream), f : F : (Fn(A) -> unit), io : Io) -> Impl : (Future[Future](unit) Io : Io)impl(Watcher, ...)
path : (Watcher) fn(self : Watcher) -> Pathis_active : (Watcher) fn(self : Watcher) -> boolpending : (Watcher) fn(self : Watcher) -> usizepoll : (Watcher) fn(self : Watcher) -> Option(FsEvent)close : (Watcher) fn(self : Watcher) -> unitStop the native watch. Queued events stay readable through poll;
next no longer waits. Idempotent, and called for you when the last
reference to the watcher goes away (Dispose below).
Parameters
| Name | Type | Notes |
|---|---|---|
self | Watcher |
Returns: unit
impl(Watcher, Stream(...))
Item : FsEventnext : (Watcher) fn(self : Watcher, io : Io) -> Impl : (Future[Future](Option(FsEvent)) Io : Io)impl(Watcher, Dispose(...))
dispose : (Watcher) fn(self : Watcher) -> unitRelease the resources self owns — a file descriptor, a socket, a lock,
a buffer the allocator handed out. Called automatically when the last
reference to the value goes away, so an implementor never calls it
directly and must tolerate being the only one who ever does.
It must be safe to run exactly once: the runtime calls it at refcount
zero, and a type that also exposes an explicit close/release is
responsible for making the second call a no-op.
Parameters
| Name | Type | Notes |
|---|---|---|
self | Watcher |
Returns: unit
Functions
Start watching path. Throws the backend's IoError (e.g. NotFound)
through exn.
Parameters
| Name | Type | Notes | Description |
|---|---|---|---|
path | Path | The watched path. | |
opts | WatchOptions | ||
exn | Exception |
Returns: Watcher