I don't think I'm confused, but let's recapitulate the thread history as I understand it:

Context: The setenv function is not thread-safe even in Rust
Question: Why doesn't Rust implement a standard library without C?
Answer: It does, but core lacks std::env, because env vars are part of an O/S
Question: Is an O/S really necessary for env vars?
Answer: Not conceptually, but without an O/S, env vars don't work as expected

I also like the sibling comment that pointed out env vars are social as much as technical. The key element is interoperability. And we haven't even discussed Windows, which has different functions and conventions for environment variables.

Now, let me address what you just said. First of all, on embedded, a freestanding C implementation is not even required to provide getenv at all. Second, while getenv is in standard C and required for hosted implementations, setenv is not. And the whole thread is really about setenv. Once we pull in setenv, we're talking not just about standard C but about POSIX, which is a specification for operating systems. I assume for the sake of fruitful discussion, we both accept that a variable put into the environment with setenv should be retrievable thereafter with getenv. This moreover should apply even if it's Rust that calls setenv and C that calls getenv and vice-versa.

So, however Rust implements environment variables should be consistent with how C implements environment variables, and since C provides the foundation for system calls and FFI for most other major languages, adhering to this convention allows interoperability across very many languages. This convention is defined by libc (the implementation of the C standard and POSIX interfaces) and thus interoperability is based on libc compatibility. So either Rust implements its own libc, which C programs would have to be (re-)compiled to use, or else it uses an existing implementation of libc, inheriting all of its quirks. Indeed, Rust targets specify the libc (or equivalent) they're using, such as -gnu, -musl, -darwin, -mingw, -msvc, etc. Linking with libraries built for a different libc on an otherwise identical platform (-gnu vs. -musl on Linux, -mingw vs -msvc on Windows) generally doesn't work and even when it appears to work leads to strange issues later. So you can't just write your own getenv and expect it to work with some other implementation of setenv.

To connect back with my other comments, there is no core::env because core assumes no libc at all. The nostd flavor of Rust (where core is available but not std) is basically equivalent to freestanding C and like freestanding C there is no interoperability guarantee, not even with freestanding C on the same hardware (indeed, the whole concept of "freestanding" is that there are no conventions to adhere to in the first place). So, std::env::set_env has the exact same problems as C setenv because it's the same thing under the hood. This cannot be addressed without fixing libc itself. Moreover, when libc is not involved, then there is no env to support to begin with.

Finally, to round out addressing what you said, core::env could exist, but probably shouldn't, for two reasons. First, it would be misleading. As I've already laid out, it would not interoperate with anything since there's nothing there to interoperate with. It would just be a global string->string map exclusive to that program, which the programmer could just as well create on his own. Second, because presumably you want it to be something other than empty, it would require some kind of global allocator, which core also assumes doesn't exist. So it would have to be something like alloc::env instead, and once you've pulled in alloc, you can just use one of the collection types (though, notably, HashMap isn't in alloc yet [1]).

[1]: https://github.com/rust-lang/rust/issues/27242