Beginner's Ownership Question

Hello,

first of all, I am pretty new in Rust (started yesterday) but I am developing software for over 35 Years now in many different languages. I really like the idea of Rust to go back to the roots and so I like to learn it (even if I will never be paid for it, because I am in a good position which hopefully will last till I retire :slight_smile:

As my first try after reading the first 10 chapters of the manual I wanted to read all files in a folder recursivly. After a few hours of try and error I managed it and the program works. But dont undertand one thing:

Here is the working code: (Please forgive me the wrong bracket format but I use this so many years I am not able to change it to this new "first brace in same line" format without getting headache)

fn main()
{
     check_files("/Users/xxx/css");
}

fn check_files(path : &str)
{
    if let Ok(entries) = read_dir(path)
    {
        for entry in entries
        {
            if let Ok(direntry) = entry
            {
                if let Ok(metadata) = direntry.metadata()
                {
                    if metadata.is_dir()
                    {
                        let pathname = direntry.path();
                        let pathname = pathname.to_str();
                        match pathname
                            {
                                Some(name) =>
                                {
                                    let pathname = name;
                                    check_files(pathname);
                                },
                                None => {}
                            }
                    }
                    else
                    {
                        let filename = direntry.file_name();
                        let filename = filename.to_str();
                        println!("{:?}", filename);
                    }
                }
            }
        }
    }
}

What takes me a while is to find out, that i have to assign direntry.path() to a local variable to make it not Temporary. Especially that I can overwrite it in the next line with the value I really need i the thing I dont understand. If I change:

    let pathname = direntry.path();
                        let pathname = pathname.to_str();
                        match pathname...

to

    let pathname = direntry.path().to_str();
                        match pathname

I get an error. In my Eyes this makes absolutly no sense. Can anyone please explain it to me?

Thanks

Claus

P.S. How the hell can I insert formatted code in a post?

To insert code use:

```rust
code goes here.
```
1 Like

Hi, to add on @chrisd, rust is the default, you can omit it.

For your issue you create an owned value and take a reference to it on the same line.
But the compiler has no reason to keep the value.
It's the same as:

let pathname = {
    let tmp = direntry.path();
    tmp.to_str()
    // here tmp is dropped
};

So you have to make a variable to hold it.

Edit: Also compiler errors give you an error number, this one is E0716 and most (all?) of them link to a more detailed explanation.

3 Likes

Hello @Thallius, and welcome to this forum :slight_smile:

That is a very legitimate question, and is one of Rust "quirks":

Bindings in Rust have a semantic meaning: the RAII pattern makes it so whenever a ressource is aquired (such as memory allocated on the heap), this ressource is owned by a binding (i.e., a variable name), so that when that binding goes out of scope, the ressource is freed.

For the sake of ergonomis, Rust allows chaining function calls, but this means that Rust will be using its own "arbitrary" bindings (I'm just saying arbitrary because the rules that govern those are not clear, at least not to me), to hold intermediate values.
Sadly the way it does it is not perfect.

For instance, in your case, it so happens that Rust may be doing something along the lines of:

let direntry_path_to_str: &str = {
    let __temp__ = direntry.path(); // owns the allocation
    __temp__.to_str()
}; // __temp__ is dropped, thus `direntry_path_to_str` would dangle

Whereas when you had bound the thing explicitely (pathname instead of __temp__), you had:

let pathname = direntry.path();
let direntry_path_to_str: &str = pathname.to_str();
// pathname still in scope, thus `direntry_path_to_str` does not dangle

Hence the difference


I know this pattern may be really surprising, but don't worry, as you saw, the borrow checker spots when the auto-generated / anonymous bindings do not last enough, so that you can just go and make them explicit :slight_smile:

4 Likes

Thanks a lot