Skip to content

Documentation gap surrounding errno #1644

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
jimrandomh opened this issue Jan 20, 2020 · 9 comments
Closed

Documentation gap surrounding errno #1644

jimrandomh opened this issue Jan 20, 2020 · 9 comments

Comments

@jimrandomh
Copy link

The README for the libc crate says:

libc provides all of the definitions necessary to easily interoperate with C code (or "C-like" code) on each of the platforms that Rust supports. This includes type definitions (e.g. c_int), constants (e.g. EINVAL) as well as function headers (e.g. malloc).

This isn't true; it doesn't provide errno. There's a large hole in the documentation surrounding this, and it's really unclear to me (as someone who just wants to call some libc functions) what the correct solution is.

Previous discussion: #47 .

The closest thing to a mention of errno in the libc crate is __errno_location (https://doc.rust-lang.org/1.6.0/libc/fn.__errno_location.html), which has leading underscores and no documentation, suggesting that it's a private API which was exposed accidentally.

The std crate has std::io::last_os_error (https://doc.rust-lang.org/std/io/struct.Error.html#method.last_os_error), which is sort-of like errno, but not quite enough like errno to actually match up with anything you'd find in manpage. It has std::io::ErrorKind (https://doc.rust-lang.org/std/io/enum.ErrorKind.html) which has error types which sound like common errnos, but if they're supposed to be the same, it has failed to document this, and it's not exhaustive. Also it's read-only, which is insufficient for many some libc functions, where the convention is to set errno to 0 before calling them.

There's an errno crate, which provides both a getter and setter, but it's by a single developer rather than the rust core team, and there's no particular reason to trust its portability or long-term stability.

There used to be a std::os::errno. It's gone, but ranks #2 in the Google search results for "rust errno" (https://www.reddit.com/r/rust/comments/31b9zw/is_stdoserrno_no_longer_available/). Just to rub it in.

The nix crate (which depends on libc and provides lots of wrappers) has nix::errno::errno and nix::errno::from_i32, which is what I've settled on for my own use. (Still readonly, and this bothers me from a soundness standpoint, but I can live with it). AFAICT nix is not by the Rust core team, but at least it's widely used and appears maintained.

Sorting through all this took a bunch of time, when all I really wanted was to call some familiar libc functions. I see a few possible options here:

  • Add errno getter and setter functions to the libc crate.
  • Document the mapping between std::io::last_os_error results and errno, make it exhaustive, and leave a note in the README saying that's where to go for errnos.
  • Change the messaging in the README to make it clear that libc is not self-contained, but requires another crate (such as errno or nix) to use correctly.
@gnzlbg
Copy link
Contributor

gnzlbg commented Jan 20, 2020

This isn't true; it doesn't provide errno.

The first line of the readme says (emphasis mine):

libc - Raw FFI bindings...

One can't bind something that doesn't exist, and on most platforms, errno is not a symbol. The search you have performed ("errno") shows the APIs that each platform provides to access it, e.g., a #[thread_local] errno on some platforms, __errno_location on others, etc. (e.g. this is how getrandom interfaces with errno portably).

If you don't want to use "Raw" bindings, then libc is not the crate that you should be using. The Rust standard library provides a platform abstraction layer, so you can use that. If you feel that std::io::last_os_error documentation is not clear, the appropriate place to fill an error is the rust-lang/rust repository. There are many crates that build platform-abstraction layers on top of libc - nix is probably the most popular.

@jimrandomh
Copy link
Author

Whether errno is a symbol is an implementation detail that programmers would not ordinarily know or care about, and the next paragraph (which I quoted) specifically says "all of the definitions necessary to easily interoperate with C code". If raw-ness means that this crate isn't directly usable, it needs to advertise itself differently.

@gnzlbg
Copy link
Contributor

gnzlbg commented Jan 20, 2020

This crate exposes Raw FFI bindings, nothing more, nothing less. If that's unclear, PRs welcome.

@JohnTitor
Copy link
Member

I'm going to close this as there's no update in over 6 months. Feel free to send a PR if you're still interested in that, of course!

@grossws
Copy link

grossws commented Jan 26, 2021

Many resources like C99 standard (see 7.5 @ page 186), cppreference C docs, MSVC libc docs, GCC docs mention that to check errno you have to clear it before library function call. Many C libraries (or ones exposing C interface) use errno to allow downstream user differentiate between error and valid call that returned some value (e.g. iterator-like interface that return NULL for end of list or error).

I found small library https://github.com/lambda-fairy/rust-errno/ but it use GetLastError/SetLastError so it wouldn't work for libc or other C libs like libsmbclient that use errno for error reporting.

Since libc already exposes __errno_location() for some platforms, it seems realistic to add wrappers like errno()/set_errno() like std does internally.

If libc maintainers aren't against adding such interface (it's required to use things like readdir and similar APIs) I'll think about PR.

@hdante
Copy link

hdante commented Dec 22, 2021

Hello, I can confirm that on December 2021 this bug still exists. On the linux version of the documentation there is no errno (and I'm assuming it doesn't exist in the code either). It has epoll_ctr(), which is also not available in most systems, but no errno. The libc crate is still broken on linux.

errno is part of the linux and POSIX APIs.

@TheWastl
Copy link

TheWastl commented Feb 3, 2022

Note that std::io::Error has a raw_os_error() function, so std::io::Error::last_os_error().raw_os_error() can be used to get errno in a portable manner.

Still, errno is definitely part of the standard C library, so IMHO it should go into libc (would be useful for no_std, for example).

@lucy
Copy link

lucy commented Jul 16, 2022

That also doesn't let you set errno = 0 which is necessary to check for errors from a lot of libc interfaces.

@jamesyoungman
Copy link

Just to emphasise lucy's point above, there are quite a lot of standard C library interfaces which cannot correctly be used without the application first setting errno. Herre are some examples:

  • readdir() - you cannot distinguish "this child is unrepresentible but there are more children" from "no more children" (this happens when the filesystem's metadata, typically the inode number, cannot be represented in the host's struct dirent). The manpage points this out, though less explicitly.
  • Functions like strtod() combine normal and error results in the same value (HUGE_VAL in the case of strtod) and so setting errno is mandatory if you want to determine whether an error has occurred.

It's all very well limiting libc to "just FFI" but if the result is an interface that cannot be correctly be used by itself, that's a really unfortunate ergonomic outcome.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants