Passing a member of a "boxed" structure to BufReader::read_to_string()


#1

I have the following structure definitions:
pub struct Estate {
row: i32,
col: i32,
pub row: Option<Box>,
}

pub struct Erw {
idx: i32,
size: i32,
buf: Option,
}

The member row in Estate is initialized using:
self.row = pub row: Option<Box>;

Now in another module, I would like to read data from a file into the field buf of Estate::row using:

let mut buf_reader = BufReader::new(fileh);

I want to use buf_reader::read_to_string() function to read the data into the member buf of Estate::row

Is is possible? If yes, how do it do it? If not, what is the alternative?

Any help will be greatly appreciated.

Thanks,
Raj Mohan


#2

Sorry, I made a typo:

pub row: Option<Box> in Estate should be 

pub row:Option<Box><Erw>>

#3
use std::io::{stdin, BufReader, Read};
struct Estate {
    row: i32,
    col: i32,
    erw: Option<Box<Erw>>,
}

struct Erw {
    idx: i32,
    size: i32,
    buf: Option<String>, // I presume this is what you meant
}

fn main() {
    let mut estate = Estate {
        row: 1,
        col: 2,
        erw: Some(Box::new(Erw {
            idx: 1,
            size: 1,
            buf: Some(String::new()),
        })),
    };
    let mut br = BufReader::new(stdin());
    // lots of unwrap()'s for demo purposes only
    br.read_to_string(estate.erw.as_mut().unwrap().buf.as_mut().unwrap());
}

Playground


#4

Thank you for your elaborate reply.
Now could you please explain me the sequences of unwrap() calls? The Rust documentation discourages the use of unwrap(). How else can the value be taken out of Option in an elegant way?

Thanks,
Raj Mohan


#5

Yeah, I left the unwraps in there to keep the code short and to show you how to get at the inner String.

Here are 3 versions to deal with the Options. You can pick how to handle the None cases accordingly (e.g. ignore, error, log, etc). Also maybe you don’t need some of these Options in the first place?

Note also that I didn’t do anything with the Result that comes back from the read_to_string call - that’s another choice for you to make on how to handle :slight_smile:.


#6

Thank you very much. The 3 versions to deal with Options was very useful. It fixed my “panic” caused by unwrap(). What could have caused the unwrap() to panic? To verify that the file was being read properly, I used BufReader:read_line() call. The program displayed the line read correctly.
But when I used the unwrap() code, it panics. Now that the “panic” is fixed, how do I verify that the data has been read correctly?
How do I now display the string buffer containing the data from the file?

 self
    .row
    .as_mut()
    .map(|e| e.chars.as_mut().map(|b| br.read_to_string(b)));

In the examples given in the Rust docs., I see that collect() has to be used to get the result back. But the function br.read_to_string(b) returns type usize. So how can I get back the buffer that was read?


#7

In the previous code if one of those Options was None then calling unwrap on it would panic. Not sure what exact code you ran so hard to say. What was the panic error message? You can also get a backtrace for panics if you enable it by setting the RUST_BACKTRACE=1environment variable.

The data was read into the Option<String> so it’s in there - no need to collect anything.


#8

Please bear with me. I am new to Rust, and hence all the questions that I have.

I ran the unwrap() version of the code after setting RUST_BACKTRACE=1. The backtrace shows that it is failing on the line with unwrap() call. As you pointed out, this is because one of the Options is “None”.

The code with unwrap() that I have is:

let mut br = BufReader::new(fileh);
let _ = br.read_to_string(self.row.as_mut().unwrap().chars.as_mut().unwrap());

The file handle, “fileh” is correct (I have verified it). So what could go wrong?

As I mentioned earlier, when I changed the code to,

let s = self
           .row
           .as_mut()
           .map(|e| e.chars.as_mut().map(|b| br.read_to_string(b)));

I don’t get the “panic”.

Now how do I see the contents of the buffer that I have read?

I derived the debug trait for the struct Erow and I tried,

println!("Read {:?}", Some(&self.row));

The output that I get is:

Read Some(Some(Erw { idx: -1, size: -1, buf: None}))

Clearly, after the br.read_to_string(), I have an empty(None) buffer. I think, this is what caused the unwrap() to panic. Why is the buffer still None?

How do I access the field “estate.erw.buf” (C style)?

Thanks,
Raj Mohan


#9

Oh, you want to create the Some(String) if it’s absent? Ok, then do this:

This will insert and return a &mut String into the buf Option if it’s None.

The previous code I gave you does nothing if any of the options are None. But how you want to handle these options, such as ignore them or create values if they’re None, depends on the semantics you want to assign. For instance, maybe you don’t even need the Option<String> to begin with - just create an Erw with an empty String to start with and then populate it from the reader.

A better suggestion could be made if you tell us how you’d like these objects to be used and populated.


#10

Ok, here is the background to the problem. I feel that the best way to learn a language is actually by writing some non-trivial program in that language. So, to learn Rust, I am working on a small text editor program. I want to read the file to be edited and populate it in:

"estate.row.buf"

I could have left out the Option. I could have initialized the field with an empty string. But as part of the learning process, I want to use Option.

Your wrote:

Oh, you want to create the Some(String) if it’s absent? Ok, then do this:

No, I don’t want to create Some(String) if it is absent. I want the field to have the line from the file just read.

The other alternative would be to read a line from the file to a temporary string and return that string to the caller. The caller then calls another function to populate “estate.erw”.

What do you suggest?

Thank you so much.

Regards,
Raj Mohan


#11

Are you going to read multiple lines? I assume so. In that case you’ll want to reuse the String that’s already in the struct so you can amortize out the backing heap allocation. So you’ll want to clear the String before filling it with the next line if the String is already present (ie you have Some(String) there).

You can read a line into a new String each time and then move it into the Erw struct - it just won’t be efficient.


#12

Thank you. I’ll try your suggestion.