Description
Proposal
Add new API: std::env::user_home_dir
returning Option<PathBuf>
.
This is a replacement for the deprecated std::env::home_dir
, providing a platform agnostic method for identifying the user's home directory checking platform-specific environment variables ('HOME' on Unix, 'USERPROFILE' on Windows) as required.
The proposed implementation is simpler than std::env::home_dir
as only searches the environment and does not attempt call one/multiple platform APIs (e.g. getpwuid_r
, GetUserProfileDirectoryW
, etc) as fallback when the appropriate environment variables are unset.
The existing env::home_dir
remains unchanged.
Problem statement
-
The
std::env::home_dir
API is deprecated. Fixing it would be a breaking API change. (See: rust-lang/rust#51656)Deprecated since 1.29.0: This function’s behavior may be unexpected on Windows.
Consider using a crate from crates.io instead. -
In most
home_dir
implementations, (includingstd::env::home_dir
) if the 'HOME' / 'USERPROFILE' environment variables are unset, there is fallback implementation that calls one/multiple platform APIs in order to guess the user's home directory. This is potentially surprising behavior, as it is not call anenv::*
function may magically get you a value which is nowhere in the process environment.- home_dir() on windows uses 'GetUserProfileDirectoryW'
- home_dir() on unix uses 'getpwuid_r()'
-
The
std::env::home_dir
API may return a value which is not usable as a Path (""
). This occurs when HOME set to an empty string, which is not uncommon in restricted shell environments (sudo, cron jobs, etc). Callers thus must check for None and for an empty string before using the returned value. Earlier documentation forstd::env::home_dir
suggests this may have been the original intended behavior:"Returns the value of the 'HOME' environment variable if it is set and not equal to the empty string."
-
Users looking for a replacement for
std::env::home_dir
are forced to evaluate and choose amongst a number of 3rd party crates:- The 'home' crate, maintained by the Cargo team, is the very popular and used by (nearly 500 crates but the Cargoteam has stated they do not wish to maintain it as a general purpose 'home_dir' replacement and they consider it only an internal Cargo and Rustup dependency.
- The 'home' crate generates a compile time error on wasm and other non-Unix/Windows platforms. The Cargo team has signaled they do not intend to fix this. See: rust-lang/cargo#12297
- Many crates do not just provide a
std::env::home_dir
replacement but also provide additional abstractions over other platform-specific APIs (XDG, Windows Known Folders API, etc) - Many crates have platform specific dependencies. For example dirs crate depends on libc and windows-sys which are entirely unused in the common case where the environment has
HOME
orUSERPROFILE
set.
Motivating examples or use cases
Developer wishes determine the current user home directory via inspecting environment variables in a platform agnostic way.
The same use case as std::env::home_dir
but without the bugs or fallback behavior when environment variables are unset.
Solution sketch
Create the API which covers the most common use of std::env::user_home_dir
getting a PathBuf
with of user's home directory, if available,
by inspecting environment variables:
- New
user_home_dir
function that returnsOption<PathBuf>
. - Checks for
USERPROFILE
environment variable on Windows. - Checks for
HOME
environment variable variable on Unix. - Return None if variable is unset.
- Return None if variable set to an empty string.
- Return None if platform does not have a concept of a home directory (wasi, etc)
- Do not attempt to guess the user's home directory when environment variables are unset (principle of least surprise)
See: env_home lib.rs for a potential implementation.
Naming is hard, especially when a deprecated API has good name. I prefer std::env::user_home_dir
as it does not collide with std::env::home_dir
or home_dir
provided by other crates.
Alternatives
-
Do nothing. Require users to evaluate and choose a 3rd party crate which provides a
home_dir
API with better behavior on Windows (usually including extra APIs):- dirs
- dirs2 - fork of 'dirs' crate
- directories - previously abandoned, revived
- directories-next - fork of 'directories' crate
- home - Won't compile on unsupported platforms (WONTFIX)
- env_home - Created as POC for this proposal.
-
Extract the current cargo implementation of
home_dir
from thehome
and trivial fixes so it compiles as a no-op on unsupported platforms. Publish as a stand-alone crate and recommend it as a replacement forstd::env::home_dir
. -
Create an implementation of
home_dir
that only relies on the platform specific APIs and does not inspect environment variables. This would make it trivial to implement the platform API fallback behavior ofstd::env::home_dir
when environment variables are unset.
Links and related work
Discussions:
- Internals: Deprecate (or break-fix) std::env::home_dir
- Deprecate
std::env::home_dir
and fix incorrect documentation rust#51656 - Fix windows home dir implementation rust#46799
- Reddit: Why env::home_dir() returns Option instead Result
- Use OS-native config, cache home directories cargo#1734
Languages whose standard library provides a platform independent home_dir abstraction:
- GoLang os.UserHomeDir()
- Python Path.home()
- Ruby Dir.home
- Java System.getProperty("user.home")
- Swift FileManager.default.homeDirectoryForCurrentUser
- ObjC NSHomeDirectory()
Languages which do not have a home_dir abstraction in the standard library:
- Perl File::HomeDir->my_home (CPAN module)
- Zig
- PHP
- DART
Notes
I am particularly interested to hear of real-world use cases or systems where $HOME is unset and the directory provided via platform-specific APIs is available/appropriate for use. In my experience $HOME
being unset is a signal that there there is nothing like a unix home directory available to my process and my app should behave accordingly.
This is my first attempt at drafting an API Change Proposal, so please be kind.
Thanks!