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(FsEventKind, ToString(...))
Methods
clone : (FsEventKind) fn(inout(self) : FsEventKind) -> FsEventKindto_string : (FsEventKind) fn(inout(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 | |
_waiters | ArrayList(Waker) | Tasks suspended in |
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(unit, Io))impl(Watcher, ...)
path : (Watcher) fn(inout(self) : Watcher) -> Pathis_active : (Watcher) fn(inout(self) : Watcher) -> boolpending : (Watcher) fn(inout(self) : Watcher) -> usizepoll : (Watcher) fn(inout(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(Option(FsEvent), Io))Wait for the next event. Resolves .None once the watcher is closed
and its queue drained. PARKS until the event callback (or close)
wakes it — no nested event loop, and no CPU while it waits. It used to
re-yield in a loop, which kept the task always runnable: a waiting
watcher spun a core and the loop never blocked
(issues/fixed/poll-and-fs-event-watches-starve-while-the-loop-waits.md).
Parameters
| Name | Type | Notes |
|---|---|---|
self | Watcher | |
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 value's
owner drops it (a value type) or the last reference to it goes away (a
reference type), 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, 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