5

When creating a new instance of a struct can you reference a previously initialized field within the struct under construction?

Example code:

use std::fs::File;
use std::io::BufReader;
use std::path::PathBuf;

#[derive(Debug)]
pub struct Test {
    file_handle: File,
    rdr: BufReader<File>,
}

impl Test {
    pub fn new(filepath: std::path::PathBuf) -> Self {
        Self {
            file_handle: File::open(&filepath)
                .expect(format!("Unable to open file {:?}", filepath).as_str()),
            rdr: BufReader::new(file_handle),
        }
    }
}

fn main() {
    let x = Test::new(PathBuf::from("my file"));
    println!("{:?}", x);
}

Error generated:

error[E0425]: cannot find value `file_handle` in this scope
  --> src/lib.rs:16:33
   |
16 |             rdr: BufReader::new(file_handle),
   |                                 ^^^^^^^^^^^ a field by this name exists in `Self`

Is there a specific pattern for this sort of thing, or is it just a Rust "no-no" (antithetical to the ideas of Rust)? How would something like this 'normally' be handled in Rust?

Update

@Netwave and @Herohtar have pointed out problems in my 'non-Rustian' expectations. Thank you, those comments both make sense and answer another un-ask question I had.

Still if I change my struct to

pub struct Test {
    a: String,
    b: String,
}

and my impl to something simple like:

pub fn new(ana: String) -> Self {
    Self {
        a: ana,
        b: a.clone(),
    }
}

I'll get a very similar error. I'm assuming Rust does not want me to use a struct field before the 'Self' block has been terminated. It seems to me the compiler could determine that the field has been set, so I suspect there is a deeper reason to prevent this behavior. I'm still in the very early stages of grok'ing the tao of Rust so I'd like to understand this (or any :-)) behavior better.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Dweeberly
  • 4,668
  • 2
  • 22
  • 41
  • 2
    short: is a nono. Longer, the BufReader moves the File iirc, so even if you could this would still block what you wanted to do. – Netwave Jan 24 '22 at 20:50
  • 1
    You can, however, use variables in the constructor to store things and reference those. That still won't help you in this situation though, because `File` is not `Copy` and will be moved. – Herohtar Jan 24 '22 at 21:10
  • That makes sense, still getting used to the automatic transfer of ownership. https://stackoverflow.com/questions/33831265/how-to-use-a-file-with-a-bufreader-and-still-be-able-to-write-to-it suggests I can access the file handle held by BufReader. – Dweeberly Jan 24 '22 at 21:11
  • 3
    Re: the update, a bare `a` isn't a meaningful identifier either during or after the `Self { }` statement. I wouldn't look at it as Rust *preventing* anything, as if the language designers thought about allowing `a` and decided not to -- that's just not the syntax to access a field, is all. – John Kugelman Jan 24 '22 at 21:33
  • I think you're confused about where the fields exist. You can't use `a` inside `new` because *that identifier doesn't exist there*. However, `String` *also* is not `Copy` so you'll have the same issue as before. – Herohtar Jan 24 '22 at 21:57

1 Answers1

2

This is not possible. In BufReader::new(file_handle) the file_handle value is moved. It doesn't get copied or cloned. Also, you can't use a &File reference in this case, but even if you could, you would have hit another well known limitation of self-referential structs.

Probably your best option is to only store BufReader and, if you need the file handler for something, get a reference to it via get_ref().

at54321
  • 8,726
  • 26
  • 46