My experiences with Rust

From: Konstantin Belousov <kostikbel_at_gmail.com>
Date: Fri, 22 Aug 2025 16:10:15 UTC
There was a long and hot thread about 'Rust in base' recently.  Since
I did used the rust language to write some very OS-specific utilities,
I decided to share my experience.  If written in C, yjr w tools would
definitely end up in base.

I do not have any huge investments in Rust, neither emotional nor
huge time spent learning it.  Still, I decided to use Rust since I was
curious and the hate/love dihotomy for just a language felt weird.

I used Rust for two utilities, first one is
https://github.com/kostikbel/pollinfo ports:sysutils/pollinfo
(disclosure: the work was sponsored by The FreeBSD Foundation)
the second one is
https://github.com/kostikbel/ktcplist ports:net/ktcplist
(another disclosure: the work was sponsored by Nvidia networking BU).

In both cases, it was a common theme to retrieve system information in
the binary format by a direct syscall invocation, then provide a user-
or machine-friendly output format for consumption.

The pollinfo utility was a part of the efforts to make event wait
observable.  There was a 'procstat kqueue' command added that reported
the kqueue(2) events that process wait for.  Similarly, a residual
tool for select(2)/(p)poll(2) interrogation felt natural.  There were
already the pre-existing kernel facilities (ptrace) that were needed
to write the tool (but transparent ptrace attach made things better,
just a disgress).

For ktcplist, the more natural action would be to extend netstat(8)
then to write a new tool, but I cannot make myself to use libxo.  It
is less painful to write Rust then to try to use libxo, at least for
me.  But first, I need to define and implement the kernel interface to
export the ktls connection list to userspace, unlike the pollinfo
case.

Nice things about using Rust:

- after typechecking, the program just worked.  This was an experience
  very similar to haskell. Of course, there were unsafe {} blocks, and
  part of the good design was to contain the unsafety to only the code
  that advanced iterators over the elements of the kernel returned
  array.  If this was followed, it was quite nice environment to work
  with WRT correctness.

Problematic parts:

1.Rust uses its own so called libc bindings.  This is a Rust crate
  containing collections of pure bindings both to libc and kernel
  interfaces.  After gettting the kernel interface done, you have to
  write separate patches for Rust libc and then work to upstream it,
  before the new kernel feature becomes useful for Rust code.  A silly
  alternative is to maintain the rust libc fork, which is not
  sustainable.  Fortunately, the libc maintainers are very responsible
  (count for a week to get a feedback).

2.Quite surprising, is the portability.  It is a known sore with C
  that you need to use %jd and (intmax_t) cast to get
  platform-independent printing of ints/longs.  For Rust, a similar
  issue is the signess of char.  When providing Rust libc binding, and
  testing either amd64, it is very tempting to use i8 as the type for
  char kernel native type, which breaks everywhere outside amd64.  You
  must use libc::c_char.  This is due to Rust type construct being an
  equivalent of C typedef.

3.It was more prominent for ktcplist then for pollinfo, so I will
  explain it there.  Nvidia verification team requested that ltcpdump
  has machine-readable output, preferrably json.  Adding serde
  serialization to json was trivial, and solved the problem, by the
  cost of adding another: making the small utility depend on several
  dozens of the external packages.  This makes it very problematic if
  we ever want to allow Rust in base and include ktcpdump as part of
  it.

I do not have any conclusions.  I am happy that both utilities live in
ports (thanks to gleb@), and they should be left there IMO.