loony-bean / textplots-rs

Terminal plotting library for Rust
238 stars 24 forks source link

Plotting in a loop #26

Open Skeletonxf opened 3 years ago

Skeletonxf commented 3 years ago

Hi

I updated my dependencies recently and code which was previously working on textplots 0.5.0 is no longer working on textplots 0.5.4

My problem is essentially that I want to break up the method chains with a loop.

let lines: Vec<_> = points.iter().map(|p| Shape::Lines(p)).collect();
for line in lines.iter() {
    chart.lineplot(line);
}
chart.display();

This strategy doesn't compile anymore.

error[E0499]: cannot borrow `chart` as mutable more than once at a time
   --> tests/bayesian_regression.rs:245:17
    |
245 |                 chart.lineplot(line);
    |                 ^^^^^ mutable borrow starts here in previous iteration of loop

error[E0499]: cannot borrow `chart` as mutable more than once at a time
   --> tests/bayesian_regression.rs:247:13
    |
245 |                 chart.lineplot(line);
    |                 ----- first mutable borrow occurs here
246 |             }
247 |             chart.display();
    |             ^^^^^
    |             |
    |             second mutable borrow occurs here
    |             first borrow later used here

I notice that the Chart used to take Shapes by value https://docs.rs/textplots/0.5.0/textplots/trait.Plot.html and it now borrows them https://docs.rs/textplots/0.5.4/textplots/trait.Plot.html

Would it be possible for you to add a second plot method which didn't return the mutable reference from the method? https://docs.rs/textplots/0.5.4/src/textplots/lib.rs.html#334

I think the returned mutable borrow while I still have the first is causing my problem.

loony-bean commented 3 years ago

Hi @Skeletonxf! I'm terribly sorry for slow response. Could I see a complete reproducible example of the issue including all the data, etc?

Skeletonxf commented 3 years ago

Hi sure

The main block of code I'm having issues with is https://github.com/Skeletonxf/easy-ml/blob/77c54c795fc4b0d37b9e15eeba37e6053cc811f8/tests/bayesian_regression.rs

When I update textplots from 0.5.2 to 0.5.3 in my Cargo.toml and recompile, I get a number of compile errors in that file that do not exist using textplots 0.5.2 or earlier.

They're initially just mismatched types

error[E0308]: mismatched types
   --> tests/bayesian_regression.rs:245:28
    |
245 |             chart.lineplot(Shape::Points(&split_for_plotting(&weights)));
    |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |                            |
    |                            expected `&Shape<'_>`, found enum `Shape`
    |                            help: consider borrowing here: `&Shape::Points(&split_for_plotting(&weights))`
let mut chart = Chart::new(80, 80, 2.0, 4.0);
chart.lineplot(Shape::Points(&split_for_plotting(&weights)));
chart.display();

If I introduce a bunch of let bindings so those errors are fixed

let mut chart = Chart::new(80, 80, 2.0, 4.0);
let points = Shape::Points(&split_for_plotting(&weights));
chart.lineplot(&points);
chart.display();

https://github.com/Skeletonxf/easy-ml/commit/87d701cc39e959953bc50b7675d5fc47e21733bf

I get even more errors, and now the borrow checker starts complaining about my use of the chart

error[E0499]: cannot borrow `chart` as mutable more than once at a time
   --> tests/bayesian_regression.rs:255:13
    |
254 |             chart.lineplot(&points);
    |             ----- first mutable borrow occurs here
255 |             chart.display();
    |             ^^^^^
    |             |
    |             second mutable borrow occurs here
    |             first borrow later used here

I can introduce even more let bindings so the only errors left are these

cannot borrow chart as mutable more than once at a time

https://github.com/Skeletonxf/easy-ml/commit/160d5bdfd738a8748f2ce01cf4653d3842bad5e9

I can fix a few of these with let bindings shadowing the chart, but I can't find a fix for when I'm using the chart in a loop.

error[E0499]: cannot borrow `*chart` as mutable more than once at a time
   --> tests/bayesian_regression.rs:181:13
    |
181 |             chart.lineplot(&all_lines_1[i]);
    |             ^^^^^ mutable borrow starts here in previous iteration of loop

https://github.com/Skeletonxf/easy-ml/commit/2c0adfb73465ef20e75ef33668e8acf43480a48c

Which leaves me with

https://github.com/Skeletonxf/easy-ml/blob/textplots-test-breakage-1/tests/bayesian_regression.rs

and three compile errors I can't fix. I think if there was alternative methods I could call that didn't return me &mut references to the chart that I didn't want to have then I could probably get rid of the compile errors using those instead.

Byter09 commented 2 years ago

I don't know if there's any active development but I used this crate for static lists of values so far but now I need it for a dynamic amount of plots, and the above error pops up because lineplot can't be used in for-loops.

Any progress on this? :/

Skeletonxf commented 2 years ago

I've fixed my dependency to textplots = "=0.5.2" until this is resolved or I find a better work around.

mkolopanis commented 1 year ago

In case you never found a reasonable workaround, you can make it work by carrying around a single mutable reference to the chart and reassigning it after every lineplot call.

Even works with colors.

use rgb::RGB8;
use textplots::{Chart, ColorPlot, Shape};

fn main(){
    println!("y = atan(x) y = Sinc(x)");
    let lines = vec![
        (
            Shape::Continuous(Box::new(|x| x.atan())),
            RGB8::new(252, 0, 0),
        ),
        (
            Shape::Continuous(Box::new(|x: f32| x.sin() / x)),
            RGB8::new(203, 0, 109),
        ),
    ];
    let mut chart = Chart::default();
    let mut ptr = &mut chart;
    for (line, color) in lines.iter() {
        ptr = ptr.linecolorplot(line, *color);
    }
    ptr.display();
}