Discussion:
[rust-dev] Mutable nested data structure with functions help
Oldřich Vetešník
2014-09-10 14:05:12 UTC
Permalink
Hello,

I have a problem with creating a mutable nested data structure when combined with functions.
I?m lost in all those &muts, boxes and lifetimes. :-)

I would like to crawl a directory looking for subdirectories and in those subdirectories find all images.
So I started with a vector for holding those subdirectories.
Then I created a struct to hold a directory path and a vector of images.
Also a struct for the image itself.

It all works in one function body but not when refactored into functions.
I understand that when a variable is sent as an argument, the ownership is moved so it cannot be used after the function call.
So I sent &-references instead but then it would yell at me that borrowed things are not mutable.
So I &mut-ed it all and it would yell that lifetimes are not defined.
So I tried to define lifetimes but then it would say that things do not live long enough. This is where I stopped.
I also tried the box-way but that yells about immutable dereference. I?m not even sure I have those boxes right.

This is the code:
(Also available here https://gist.github.com/ollie/9c81d2a368bf7bf0831a, there is also a Ruby version not listed here.)


With references and lifetimes:

#[deriving(Show)]
struct Image {
filename: String,
}

#[deriving(Show)]
struct Section<'a> {
path: String,
images: &'a mut Vec<Image>,
}

impl Image {
fn new(filename: &str) -> Image {
Image {
filename: String::from_str(filename),
}
}
}

impl<'a> Section<'a> {
fn new(path: &str) -> Section {
Section {
path: String::from_str(path),
images: &mut Vec::new(),
}
}

fn add_image(&mut self, image: Image) {
self.images.push(image);
}
}

fn read_directories(sections: &mut Vec<&mut Section>) {
let dirs = ["./dir-a", "./dir-b"];

for dir in dirs.iter() {
let mut section = &mut Section::new(*dir);
read_images(section);
sections.push(section);
}
}

fn read_images(section: &mut Section) {
let files = ["./image-1.png", "./image-2.png"];

for file in files.iter() {
section.add_image(Image::new(*file));
}
}

fn main() {
let mut sections = Vec::new();
read_directories(&mut sections);
println!("{}", sections);
}



With boxes:

#[deriving(Show)]
struct Image {
filename: String,
}

#[deriving(Show)]
struct Section {
path: String,
images: Box<Vec<Image>>,
}

impl Image {
fn new(filename: &str) -> Image {
Image {
filename: String::from_str(filename),
}
}
}

impl Section {
fn new(path: &str) -> Section {
Section {
path: String::from_str(path),
images: box Vec::new(),
}
}

fn add_image(&mut self, image: Image) {
self.images.push(image);
}
}

fn read_directories(sections: Box<Vec<Box<Section>>>) {
let dirs = ["./dir-a", "./dir-b"];

for dir in dirs.iter() {
let mut section = box Section::new(*dir);
read_images(section);
sections.push(section);
}
}

fn read_images(section: Box<Section>) {
let files = ["./image-1.png", "./image-2.png"];

for file in files.iter() {
section.add_image(Image::new(*file));
}
}

fn main() {
let mut sections = box Vec::new();
read_directories(sections);
println!("{}", sections);
}



All in one, no referenes and lifetimes:

#[deriving(Show)]
struct Image {
filename: String,
}

#[deriving(Show)]
struct Section {
path: String,
images: Vec<Image>,
}

impl Image {
fn new(filename: &str) -> Image {
Image {
filename: String::from_str(filename),
}
}
}

impl Section {
fn new(path: &str) -> Section {
Section {
path: String::from_str(path),
images: Vec::new(),
}
}

fn add_image(&mut self, image: Image) {
self.images.push(image);
}
}

fn main() {
let mut sections = Vec::new();
let dirs = ["./dir-a", "./dir-b"];

for dir in dirs.iter() {
let mut section = Section::new(*dir);
let files = ["./image-1.png", "./image-2.png"];

for file in files.iter() {
section.add_image(Image::new(*file));
}

sections.push(section);
}

println!("{}", sections);

// [
// Section {
// path: ./dir-a,
// images: [Image { filename: ./image-1.png }, Image { filename: ./image-2.png }]
// },
// Section {
// path: ./dir-b,
// images: [Image { filename: ./image-1.png }, Image { filename: ./image-2.png }]
// }
// ]
}


I?ve read the tutorial, the rust-by-example site but it still feels alien to me. :-/

Thank you,
Ollie
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/rust-dev/attachments/20140910/bee6a64e/attachment.html>
Vladimir Matveev
2014-09-10 15:17:01 UTC
Permalink
Hi, Ollie,

The working piece of code code: http://is.gd/dX7hzB

First of all, try to go through the official Rust guide [1]. It is mostly finished and it is *really* good. In particular, it explains what ownership is and how it should be managed.

I?ve took your `example_box.rs` as a base and modified it as follows:
1. I?ve removed `Box`es everywhere. You shouldn?t use them unless you really know why you need them. You can read more on boxes and other kinds of pointers here [2]. You will need to do it anyway if you go through the guide [1] anyway.
2. I?ve made your functions to accept &mut Vec<Section> and &mut Section. This is exactly what ownership is about: these functions should not take ownership of their arguments, but they need to modify them, so &mut is the solution.

Apart from that your code is mostly fine.

Hope that helps!

[1]: http://doc.rust-lang.org/guide.html
[2]: http://doc.rust-lang.org/guide-pointers.html
Post by Oldřich Vetešník
Hello,
I have a problem with creating a mutable nested data structure when combined with functions.
I?m lost in all those &muts, boxes and lifetimes. :-)
I would like to crawl a directory looking for subdirectories and in those subdirectories find all images.
So I started with a vector for holding those subdirectories.
Then I created a struct to hold a directory path and a vector of images.
Also a struct for the image itself.
It all works in one function body but not when refactored into functions.
I understand that when a variable is sent as an argument, the ownership is moved so it cannot be used after the function call.
So I sent &-references instead but then it would yell at me that borrowed things are not mutable.
So I &mut-ed it all and it would yell that lifetimes are not defined.
So I tried to define lifetimes but then it would say that things do not live long enough. This is where I stopped.
I also tried the box-way but that yells about immutable dereference. I?m not even sure I have those boxes right.
(Also available here https://gist.github.com/ollie/9c81d2a368bf7bf0831a, there is also a Ruby version not listed here.)
#[deriving(Show)]
struct Image {
filename: String,
}
#[deriving(Show)]
struct Section<'a> {
path: String,
images: &'a mut Vec<Image>,
}
impl Image {
fn new(filename: &str) -> Image {
Image {
filename: String::from_str(filename),
}
}
}
impl<'a> Section<'a> {
fn new(path: &str) -> Section {
Section {
path: String::from_str(path),
images: &mut Vec::new(),
}
}
fn add_image(&mut self, image: Image) {
self.images.push(image);
}
}
fn read_directories(sections: &mut Vec<&mut Section>) {
let dirs = ["./dir-a", "./dir-b"];
for dir in dirs.iter() {
let mut section = &mut Section::new(*dir);
read_images(section);
sections.push(section);
}
}
fn read_images(section: &mut Section) {
let files = ["./image-1.png", "./image-2.png"];
for file in files.iter() {
section.add_image(Image::new(*file));
}
}
fn main() {
let mut sections = Vec::new();
read_directories(&mut sections);
println!("{}", sections);
}
#[deriving(Show)]
struct Image {
filename: String,
}
#[deriving(Show)]
struct Section {
path: String,
images: Box<Vec<Image>>,
}
impl Image {
fn new(filename: &str) -> Image {
Image {
filename: String::from_str(filename),
}
}
}
impl Section {
fn new(path: &str) -> Section {
Section {
path: String::from_str(path),
images: box Vec::new(),
}
}
fn add_image(&mut self, image: Image) {
self.images.push(image);
}
}
fn read_directories(sections: Box<Vec<Box<Section>>>) {
let dirs = ["./dir-a", "./dir-b"];
for dir in dirs.iter() {
let mut section = box Section::new(*dir);
read_images(section);
sections.push(section);
}
}
fn read_images(section: Box<Section>) {
let files = ["./image-1.png", "./image-2.png"];
for file in files.iter() {
section.add_image(Image::new(*file));
}
}
fn main() {
let mut sections = box Vec::new();
read_directories(sections);
println!("{}", sections);
}
#[deriving(Show)]
struct Image {
filename: String,
}
#[deriving(Show)]
struct Section {
path: String,
images: Vec<Image>,
}
impl Image {
fn new(filename: &str) -> Image {
Image {
filename: String::from_str(filename),
}
}
}
impl Section {
fn new(path: &str) -> Section {
Section {
path: String::from_str(path),
images: Vec::new(),
}
}
fn add_image(&mut self, image: Image) {
self.images.push(image);
}
}
fn main() {
let mut sections = Vec::new();
let dirs = ["./dir-a", "./dir-b"];
for dir in dirs.iter() {
let mut section = Section::new(*dir);
let files = ["./image-1.png", "./image-2.png"];
for file in files.iter() {
section.add_image(Image::new(*file));
}
sections.push(section);
}
println!("{}", sections);
// [
// Section {
// path: ./dir-a,
// images: [Image { filename: ./image-1.png }, Image { filename: ./image-2.png }]
// },
// Section {
// path: ./dir-b,
// images: [Image { filename: ./image-1.png }, Image { filename: ./image-2.png }]
// }
// ]
}
I?ve read the tutorial, the rust-by-example site but it still feels alien to me. :-/
Thank you,
Ollie
_______________________________________________
Rust-dev mailing list
Rust-dev at mozilla.org
https://mail.mozilla.org/listinfo/rust-dev
Oldřich Vetešník
2014-09-11 07:25:50 UTC
Permalink
Hi Vladimir,

thanks for the pointers guide, I forgot about it. I remember reading about it in the ?normal? guide but didn?t really understand it.
Hopefully if I read it a few times, it will sink in. :-)

Ollie
Post by Vladimir Matveev
Hi, Ollie,
The working piece of code code: http://is.gd/dX7hzB
First of all, try to go through the official Rust guide [1]. It is mostly finished and it is *really* good. In particular, it explains what ownership is and how it should be managed.
1. I?ve removed `Box`es everywhere. You shouldn?t use them unless you really know why you need them. You can read more on boxes and other kinds of pointers here [2]. You will need to do it anyway if you go through the guide [1] anyway.
2. I?ve made your functions to accept &mut Vec<Section> and &mut Section. This is exactly what ownership is about: these functions should not take ownership of their arguments, but they need to modify them, so &mut is the solution.
Apart from that your code is mostly fine.
Hope that helps!
[1]: http://doc.rust-lang.org/guide.html
[2]: http://doc.rust-lang.org/guide-pointers.html
Post by Oldřich Vetešník
Hello,
I have a problem with creating a mutable nested data structure when combined with functions.
I?m lost in all those &muts, boxes and lifetimes. :-)
I would like to crawl a directory looking for subdirectories and in those subdirectories find all images.
So I started with a vector for holding those subdirectories.
Then I created a struct to hold a directory path and a vector of images.
Also a struct for the image itself.
It all works in one function body but not when refactored into functions.
I understand that when a variable is sent as an argument, the ownership is moved so it cannot be used after the function call.
So I sent &-references instead but then it would yell at me that borrowed things are not mutable.
So I &mut-ed it all and it would yell that lifetimes are not defined.
So I tried to define lifetimes but then it would say that things do not live long enough. This is where I stopped.
I also tried the box-way but that yells about immutable dereference. I?m not even sure I have those boxes right.
(Also available here https://gist.github.com/ollie/9c81d2a368bf7bf0831a, there is also a Ruby version not listed here.)
#[deriving(Show)]
struct Image {
filename: String,
}
#[deriving(Show)]
struct Section<'a> {
path: String,
images: &'a mut Vec<Image>,
}
impl Image {
fn new(filename: &str) -> Image {
Image {
filename: String::from_str(filename),
}
}
}
impl<'a> Section<'a> {
fn new(path: &str) -> Section {
Section {
path: String::from_str(path),
images: &mut Vec::new(),
}
}
fn add_image(&mut self, image: Image) {
self.images.push(image);
}
}
fn read_directories(sections: &mut Vec<&mut Section>) {
let dirs = ["./dir-a", "./dir-b"];
for dir in dirs.iter() {
let mut section = &mut Section::new(*dir);
read_images(section);
sections.push(section);
}
}
fn read_images(section: &mut Section) {
let files = ["./image-1.png", "./image-2.png"];
for file in files.iter() {
section.add_image(Image::new(*file));
}
}
fn main() {
let mut sections = Vec::new();
read_directories(&mut sections);
println!("{}", sections);
}
#[deriving(Show)]
struct Image {
filename: String,
}
#[deriving(Show)]
struct Section {
path: String,
images: Box<Vec<Image>>,
}
impl Image {
fn new(filename: &str) -> Image {
Image {
filename: String::from_str(filename),
}
}
}
impl Section {
fn new(path: &str) -> Section {
Section {
path: String::from_str(path),
images: box Vec::new(),
}
}
fn add_image(&mut self, image: Image) {
self.images.push(image);
}
}
fn read_directories(sections: Box<Vec<Box<Section>>>) {
let dirs = ["./dir-a", "./dir-b"];
for dir in dirs.iter() {
let mut section = box Section::new(*dir);
read_images(section);
sections.push(section);
}
}
fn read_images(section: Box<Section>) {
let files = ["./image-1.png", "./image-2.png"];
for file in files.iter() {
section.add_image(Image::new(*file));
}
}
fn main() {
let mut sections = box Vec::new();
read_directories(sections);
println!("{}", sections);
}
#[deriving(Show)]
struct Image {
filename: String,
}
#[deriving(Show)]
struct Section {
path: String,
images: Vec<Image>,
}
impl Image {
fn new(filename: &str) -> Image {
Image {
filename: String::from_str(filename),
}
}
}
impl Section {
fn new(path: &str) -> Section {
Section {
path: String::from_str(path),
images: Vec::new(),
}
}
fn add_image(&mut self, image: Image) {
self.images.push(image);
}
}
fn main() {
let mut sections = Vec::new();
let dirs = ["./dir-a", "./dir-b"];
for dir in dirs.iter() {
let mut section = Section::new(*dir);
let files = ["./image-1.png", "./image-2.png"];
for file in files.iter() {
section.add_image(Image::new(*file));
}
sections.push(section);
}
println!("{}", sections);
// [
// Section {
// path: ./dir-a,
// images: [Image { filename: ./image-1.png }, Image { filename: ./image-2.png }]
// },
// Section {
// path: ./dir-b,
// images: [Image { filename: ./image-1.png }, Image { filename: ./image-2.png }]
// }
// ]
}
I?ve read the tutorial, the rust-by-example site but it still feels alien to me. :-/
Thank you,
Ollie
_______________________________________________
Rust-dev mailing list
Rust-dev at mozilla.org (mailto:Rust-dev at mozilla.org)
https://mail.mozilla.org/listinfo/rust-dev
Loading...