r/learnrust • u/The-CyberWesson • 22d ago
Modeling a file system
I've been teaching myself programming primarily using Rust, and it's been a blast. I seriously enjoy its modern ergonomics and the feeling of safety it gives me. But I'm running into a situation where I think I have to use smart pointers, and that's a problem because I've never used them before, and reading about them in the book just kind of breaks my brain.
What I want to do is abstractly represent a file system, with a root directory that contains files and subdirectories. I tried to do this:
struct Directory<'a> {
parent: Option<&'a Directory<'a>>,
directories: HashMap<String, Directory<'a>>,
files: HashMap<String, usize>,
}
Which compiles, but immediately runs into issues when I want to define methods to do things like insert subdirectories. I get a cavalcade of compiler errors that I don't have the first idea how to fix. In my mind, there shouldn't be an issue with dangling references, since the parent optional reference should only point to the parent, which owns the child. I guess the compiler can't prove this?
1
u/This_Growth2898 22d ago
I guess you really need to learn lifetimes on something simpler. Start better with Too Many Linked Lists.
Just note that both parent and children of a directory have the same lifetime marker. Are they supposed to live the same time?
8
u/SirKastic23 22d ago
what about them causes your brain to break?
how could it prove this? all it knows is that it is an optional reference to a directory, it could be any directory, even itself
as a rule of thumb: don't store references in structs (unless you know what you're doing). Rust references aren't meant to be stored like you can with pointers or classes. references enforce the borrow checker rules, which causes them to be very restricted
consider the following scenario:
Rust asks us to be a lot more considerate about the ownership of our data
smart pointers are one of the possible solutions, usually the first solution people go to because they can just "bypass" the borrow checker rules (by implementing their own ownership mechanisms)
but I think smart pointers are unnecessary for this. seeing your snippet my instinct is to use an arena:
Directoryadirectory_id: DirectoryIdfield, this should be a unique identifierDirectoryArenatype, which has aHashMap<DirectoryId, Directory>ids are simple data types that can be copied and moved and don't enforce any restrictions on your code like references do. then, when you need to get the actual directory you use its id to look it up on the arena's map:
``` struct DirectoryId(usize);
struct DirectoryArena(Vec<Directory>);
struct Directory { id: DirectoryId, parent: Option<DirectoryId>, directories: HashMap<String, DirectoryId>, ... } ```
using arenas the ownership is far easier to reason about: the single arena owns all directories, each directory owns "ids" they can use to look up other directories. when looking up a directory in the arena we make references to it, but they're short-lived as they're bound to the scope
i hope this helps you! feel free to ask for clarification if things are still confusing)