[][src]Module users::cache

A cache for users and groups provided by the OS.

Because the users table changes so infrequently, it's common for short-running programs to cache the results instead of getting the most up-to-date entries every time. The UsersCache type helps with this, providing methods that have the same name as the others in this crate, only they store the results.

Example

use std::sync::Arc;
use users::{Users, UsersCache};

let mut cache = UsersCache::new();
let user      = cache.get_user_by_uid(502).expect("User not found");
let same_user = cache.get_user_by_uid(502).unwrap();

// The two returned values point to the same User
assert!(Arc::ptr_eq(&user, &same_user));

Caching, multiple threads, and mutability

The UsersCache type is caught between a rock and a hard place when it comes to providing references to users and groups.

Instead of returning a fresh User struct each time, for example, it will return a reference to the version it currently has in its cache. So you can ask for User #501 twice, and you’ll get a reference to the same value both time. Its methods are idempotent -- calling one multiple times has the same effect as calling one once.

This works fine in theory, but in practice, the cache has to update its own state somehow: it contains several HashMaps that hold the result of user and group lookups. Rust provides mutability in two ways:

  1. Have its methods take &mut self, instead of &self, allowing the internal maps to be mutated (“inherited mutability”)
  2. Wrap the internal maps in a RefCell, allowing them to be modified (“interior mutability”).

Unfortunately, Rust is also very protective of references to a mutable value. In this case, switching to &mut self would only allow for one user to be read at a time!

use users::{Users, Groups, UsersCache};

let mut cache = UsersCache::new();

let uid   = cache.get_current_uid();                          // OK...
let user  = cache.get_user_by_uid(uid).unwrap();              // OK...
let group = cache.get_group_by_gid(user.primary_group_id());  // No!

When we get the user, it returns an optional reference (which we unwrap) to the user’s entry in the cache. This is a reference to something contained in a mutable value. Then, when we want to get the user’s primary group, it will return another reference to the same mutable value. This is something that Rust explicitly disallows!

The compiler wasn’t on our side with Option 1, so let’s try Option 2: changing the methods back to &self instead of &mut self, and using RefCells internally. However, Rust is smarter than this, and knows that we’re just trying the same trick as earlier. A simplified implementation of a user cache lookup would look something like this:

fn get_user_by_uid(&self, uid: uid_t) -> Option<&User> {
    let users = self.users.borrow_mut();
    users.get(uid)
}

Rust won’t allow us to return a reference like this because the Ref of the RefCell just gets dropped at the end of the method, meaning that our reference does not live long enough.

So instead of doing any of that, we use Arc everywhere in order to get around all the lifetime restrictions. Returning reference-counted users and groups mean that we don’t have to worry about further uses of the cache, as the values themselves don’t count as being stored in the cache anymore. So it can be queried multiple times or go out of scope and the values it produces are not affected.

Structs

UsersCache

A producer of user and group instances that caches every result.