Is there a stable type like a HashMap where the key can be of Principal type and the value a custom type?

Basically seeking something like this:

stable var users : HashMap.HashMap<Principal, UserData> = HashMap.empty();

Where for example

type UserData = {
    principal : Principal;
    favorite_color : Text;
    name : Text;
    favorite_number : Nat;
  };

Not that I have seen.

I know there was a stable b-tree implementation but i havent used it myself

Otherwise what you would do is have a stable variable that was like a array or another stable data structure

Thank you, I ended up using this for now. As far as I can tell, HashMap is a far better structure than Trie to store principals as keys.

My worry are bugs and security vulnerabilities since it seems to be a little known repo, but really the natural structure is HashMap so I’ll use this for now.

How about RBTree? (Which should perhaps better be named TreeMap or something like that.)

We use both motoko-hash-map and the migration pattern in the latest version of axon. See: axon/lib.mo at da8601a65fce0d2536971bca562538fa39f1cd53 · icdevs/axon · GitHub for the ledger implementation.

Is it stable?

I see

stable var rb_users = RBTree.RBTree<Principal, UserData>(Principal.compare);

variable rb_users is declared stable but has non-stable type
  RBTree<Principal, UserData>Motoko

Are there major advantages of doing this over StableHashMap?

Likely yes. I haven’t tested it against that library explicitly, but check this thread: ∞ Day - Origyn Motoko Gift 2 - A Better Map

I work with something like this:

For the types

/// Users
    public type UserID   = Principal;
    public type Username = Text;
    public type UserData = {
        username : Username;
        banned   : Bool;
    };

The HashMaps

/// We create an array as a stable var, since hashmaps are not stable
private stable var _users : [(UserID, UserData)] = [];

/// Then we create the hashmap and initialize it from the _users array
var users : HashMap.HashMap<UserID, UserData> = HashMap.fromIter(_users.vals(), 0, Principal.equal, Principal.hash);

And to keep them on every update

//State functions
    system func preupgrade() {
        /// Before an update we put all values from the hashmap to the array, so they can be preserved
        _users := Iter.toArray(users.entries());
    };
    system func postupgrade() {
        /// And since the hashmap is initialized with all the values from the stable array, we can reset the array so in the next update values don't duplicate
        _users := [];
    };

I hope this can help with your main problem (:

Thank you, I ended up using this on @skilesare’s advice elsewhere, which I believe doesn’t require pre-postupgrade: GitHub - ZhenyaUsenko/motoko-hash-map: Stable hash maps for Motoko

This is working well, I added below

let users = HashMap.HashMap<Principal, User>(0, Principal.equal, Principal.hash);
private stable var _users : [(Principal, User)] = ;

//State functions
system func preupgrade() {
/// Before an update we put all values from the hashmap to the array, so they can be preserved
_users := Iter.toArray(users.entries());
};
system func postupgrade() {
/// And since the hashmap is initialized with all the values from the stable array, we can reset the array so in the next update values don’t duplicate
for ( (p, al ) in _users.vals() ) {
users.put(p, al);
};
_users := ;
};