I'm obviosly missing some fundimental concept with rust. I'm pretty experienced with c++ and maybe that's whats throwing me. I've implemented this code several times now. The only time it was truely successful was when I used an index in the Tile struct rather than a refererence. I just don't like the idea of using a index when I should be able to use a reference. In C++ this would be no problem but in rust it really hates the idea of storing a reference in a struct.
The error I get is:
error[E0502]: cannot borrow `tiler` as immutable because it is also borrowed as mutable
--> src\main.rs:139:5
|
138 | if tiler.calc((768,768)){
| --------------------- mutable borrow occurs here
139 | tiler.write_image(PathBuf::from("out.png"));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| immutable borrow occurs here
| mutable borrow later used here
and here's the code:
use std::path::PathBuf;
use std::collections::BTreeSet;
type Position=(u32,u32);
type Extent=(u32,u32);
type Bound=(u32,u32,u32,u32);
#[derive(Debug)]
struct Image {
file: PathBuf,
dimension: Extent,
}
#[derive(Debug)]
struct Tile <'a>{
image: &'a Image,
bound: Bound
}
struct Tiler <'a>{
images: Vec<Image>,
tiles: Vec<Tile<'a>>,
dimension: Extent,
}
fn bound(pos:Position,sze:Extent)->Bound{
(pos.0,pos.1,pos.0+sze.0,pos.1+sze.1)
}
impl<'a> Tiler <'a>{
fn new() -> Self {
Tiler {
images: Vec::new(),
tiles: Vec::new(),
dimension: (0, 0),
}
}
fn set_files(&mut self, files: &[PathBuf]) {
for file in files {
match imagesize::size(file) {
Ok(d) => self.images.push(Image {
file: file.to_path_buf(),
dimension: (d.width as u32, d.height as u32),
}),
Err(why) => {
println!("{}", why)
}
}
}
self.images.sort_by(|a, b| {
(b.dimension.0 * b.dimension.0 / b.dimension.1)
.cmp(&(a.dimension.0 * a.dimension.0 / a.dimension.1))
});
}
fn calc(&'a mut self, dimension: (u32, u32)) -> bool {
self.dimension = dimension;
let mut xset = BTreeSet::new();
let mut yset = BTreeSet::new();
xset.insert(0u32);
yset.insert(0u32);
self.images.iter().all(|image| {
let mut want = (0u32, 0u32, 0u32, 0u32);
let found = yset.iter().any(|y| {
xset.iter().any(|x| {
want = bound((*x,*y),image.dimension);
if want.2>dimension.0 {return false;}
if want.3>dimension.1 {return false;}
self.tiles.iter().all(|t| {
want.0 >= t.bound.2
|| want.1 >= t.bound.3
|| want.2 <= t.bound.0
|| want.3 <= t.bound.1
})
})
});
self.tiles.push(Tile {
image,
bound:want
});
xset.insert(want.2);
yset.insert(want.3);
found
})
}
fn as_ratios(&self)->Vec<(f32,f32,f32,f32)>{
let mut out=Vec::new();
let full=(self.dimension.0 as f32,self.dimension.1 as f32);
for tile in &self.tiles{
out.push((tile.bound.0 as f32/full.0,
tile.bound.1 as f32/full.1,
tile.bound.2 as f32/full.0,
tile.bound.3 as f32/full.1,))
}
out
}
fn write_image(&self, out:PathBuf) {
let out_image = &mut image::RgbaImage::new(self.dimension.0, self.dimension.1);
for tile in &self.tiles {
let in_image = &image::io::Reader::open(tile.image.file.as_path())
.unwrap()
.decode()
.unwrap();
image::imageops::overlay(
out_image,
in_image,
tile.bound.0 as i64,
tile.bound.1 as i64,
);
}
match image::save_buffer(
out,
out_image,
self.dimension.0,
self.dimension.1,
image::ColorType::Rgba8,
) {
Ok(_) => {}
Err(e) => {
println!("Failed to save file. {}", e);
}
}
}
}
fn main(){
let paths = [
PathBuf::from("a.png"),
PathBuf::from("R.png"),
PathBuf::from("b.png"),
PathBuf::from("s.png"),
PathBuf::from("t.png"),
PathBuf::from("c.png"),
PathBuf::from("d.png"),
PathBuf::from("a.png"),
PathBuf::from("R.png"),
PathBuf::from("b.png"),
];
let mut tiler = Tiler::new();
tiler.set_files(paths.as_slice());
if tiler.calc((768,768)){
tiler.write_image(PathBuf::from("out.png"));
}
}