Search code examples
stringrustplotlifetime

How to dynamically use plotter SegmentValue?


I have a struct Band:

pub struct Band{
    pub name: String,
    pub start_dt: NaiveDateTime, 
    pub end_dt: NaiveDateTime, 
    pub stage: String,
    pub selected: bool,
}

I'm using the plotters crate to plot. I have a vector of Band objects and want to plot rectangles. The chart for my plot has DateTime on the y axis and Segmented values with Stage names on the x axis:

let mut chart = ChartBuilder::on(&drawing_area)
    .caption(current_day, ("Arial", 30))
    .set_label_area_size(LabelAreaPosition::Left, 40)
    .set_label_area_size(LabelAreaPosition::Bottom, 60)
    // the x axis are the sages. we want the names centered
    // on an entry (i.e. it starts with half in front of a segment
    // and ends half after a segment, rather than matching segments 
    // exactle)
    // ----|---------------|-------------|-------
    //   main stage     t stage      wera stage 
    // for that behaviour, we need the into_segmented() command
    .build_cartesian_2d(stages.into_segmented(), first_utc..last_utc)
    .unwrap();

How can I get my entire vector to plot their rectangles? Whatever I do, rust complains about me using incorrect strings and thus not fulfilling trait bounds. I have wasted two hours on this and don't know how to make progress anymore.


Following are a few of the tries I had (not in the order I tried them, but whatever).

  1. For-loop with a single element-vector
    for band in bands{
        let stage = band.stage.clone();
        chart.draw_series(vec![
            Rectangle::new(
                [
                    (SegmentValue::Exact(&stage), band.start_dt.and_utc()),
                    (SegmentValue::CenterOf(&stage), band.end_dt.and_utc())
                ],
                Into::<ShapeStyle>::into(BLACK)
            )
        ]).unwrap();
    }
|| the trait bound `for<'b> &'b  plotters::element::Rectangle<(plotters::prelude::SegmentValue<&String>, DateTime<Utc>)>: PointCollection<'b, (plotters::prelude::SegmentValue<&&str>, DateTime<Utc>)>` is not satisfied 
||     | 
|| 97  |         chart.draw_series(vec![ 
||     |   ^^^^^^^^^^^ the trait `for<'b> PointCollection<'b, (plotters::prelude::SegmentValue<&&str>, DateTime<Utc>)>` is not implemented for `&'b plotters::element::Rectangle<(plotters::prelude::SegmentValue<&String>, DateTime<Utc>)>` 
||     | 
||     = help: the trait `PointCollection<'_, (plotters::prelude::SegmentValue<&String>, DateTime<Utc>)>` is implemented for `&plotters::element::Rectangle<(plotters::prelude::SegmentValue<&String>, DateTime<Utc>)>` 
||     = help: for that trait implementation, expected `String`, found `&str` 
|| required by a bound in `ChartContext::<'a, DB, CT>::draw_series` 
||     | 
|| 120 |     pub fn draw_series<B, E, R, S>( 
||     |            ----------- required by a bound in this associated function 
|| ... 
|| 126 |         for<'b> &'b E: PointCollection<'b, CT::From, B>, 
||     |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ChartContext::<'a, DB, CT>::draw_series`
  1. Same as 1, but going for the required string type
    for band in bands{
        let stage = band.stage.clone();
        chart.draw_series(vec![
            Rectangle::new(
                [
                    (SegmentValue::Exact(&stage.as_str()), band.start_dt.and_utc()),
                    (SegmentValue::CenterOf(&band.stage.as_str()), band.end_dt.and_utc())
                ],
                Into::<ShapeStyle>::into(BLACK)
            )
        ]).unwrap();
    }

Lifetime of stage doesn't suffice

|| `stage` does not live long enough 
||     | 
|| 96  |         let stage = band.stage.clone(); 
||     |             ----- binding `stage` declared here 
|| 97  |         chart.draw_series(vec![ 
||     |  
||   
----- borrow later used here 
|| ... 
|| 100 |                     (SegmentValue::Exact(&stage.as_str()), band.start_dt.and_utc()), 
||    |                                           ^^^^^ borrowed value does not live long enough 
|| ... 
|| 106 |     } 
||     |     - `stage` dropped here while still borrowed 
|| temporary value dropped while borrowed 
||     | 
|| 97  |         chart.draw_series(vec![ 
||     |   
----- borrow later used here 
|| ... 
|| 100 |                     (SegmentValue::Exact(&stage.as_str()), band.start_dt.and_utc()), 
||    |                                           ^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use 
|| ... 
|| 105 |      ]).unwrap(); 
||     |                    - temporary value is freed at the end of this statement 
||     | 
||     = note: consider using a `let` binding to create a longer lived value 
|| `band.stage` does not live long enough 
||     | 
|| 95  |     for band in bands{ 
||     |    
---- binding `band` declared here 
|| 96  |         let stage = band.stage.clone(); 
|| 97  |         chart.draw_series(vec![ 
||     | 
----- borrow later used here 
|| ... 
|| 101 |                     (SegmentValue::CenterOf(&band.stage.as_str()), band.end_dt.and_utc()) 
||     |                                              ^^^^^^^^^^ borrowed value does not live long enough 
|| ... 
|| 106 |     } 
||     |     - `band.stage` dropped here while still borrowed 
|| temporary value dropped while borrowed 
||     | 
|| 97  |         chart.draw_series(vec![ 
||     |         ----- borrow later used here 
|| ... 
|| 101 |                     (SegmentValue::CenterOf(&band.stage.as_str()), band.end_dt.and_utc()) 
||     |                                              ^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use 
|| ... 
|| 105 |         ]).unwrap(); 
||     |            
- temporary value is freed at the end of this statement 
||     | 
||     = note: consider using a `let` binding to create a longer lived value
  1. Enclosure of the bands vector
    chart.draw_series(
        bands.iter().map(|band| {
            Rectangle::new(
                [((SegmentValue::Exact(&band.stage.as_str())).clone(), band.start_dt.and_utc()),
                    ((SegmentValue::CenterOf(&band.stage.as_str())).clone(), band.end_dt.and_utc())],
                Into::<ShapeStyle>::into(BLACK).filled()
            )
        })
    ).unwrap();

Returns a value that still references a temporary value

|| cannot return value referencing temporary value 
||     | 
|| 99  | / Rectangle::new( 
|| 100 | |                 [((SegmentValue::Exact(&band.stage.as_str())).clone(), band.start_dt.and_utc()), 
|| 101  
|| | |                     ((SegmentValue::CenterOf(&band.stage.as_str())).clone(), band.end_dt.and_utc())], 
||     | |                                   
------------------- temporary value created here 
|| 102 | |                 Into::<ShapeStyle>::into(BLACK).filled() 
|| 103 | |             ) 
||   | |_____________^ returns a value referencing data owned by the current function 
|| cannot return value referencing temporary value 
|| | 
|| 99  | /             Rectangle::new( 
|| 100 | |                 [((SegmentValue::Exact(&band.stage.as_str())).clone(), band.start_dt.and_utc()), 
||     | |                                  
------------------- temporary value created here 
|| 101 | |                     ((SegmentValue::CenterOf(&band.stage.as_str())).clone(), band.end_dt.and_utc())], 
|| 102 | |                 Into::<ShapeStyle>::into(BLACK).filled() 
|| 103 | |             ) 
||   | |_____________^ returns a value referencing data owned by the current function

The crate has a (GitHub) forum at which you get no answers, otherwise I would have asked over there...


Solution

  • The problem I had was mostly regarding input data. Originally, for testing I used stages.into_segmented() where stages was an array of &str elements:

    let stages = [ 
        "Main Stage", 
        "T Stage", 
        "Wera Stage" 
    ];
    

    Thus, the into_segmented() call created SegmentValue(&&str) elements and I still don't know how to solve the issue for this data type.

    However, once I realized this data type was problematic, I changed it to a vector of strings:

    let stages: Vec<String> = vec![ 
        String::from("Main Stage"), 
        String::from("T Stage"), 
        String::from("Wera Stage") 
    ];
    

    With this, getting the SegmentValue to accept my dynamic input was rather trivial. I can now borrow the stage String from my Band struct and be done with it. Using a closure, it looks like this:

        // draw all bands into the chart
        chart.draw_series(
            bands.iter().map(|band| {
    
                Rectangle::new(
                    [
                        (SegmentValue::Exact(&band.stage), band.start_dt.and_utc()),
                        (SegmentValue::CenterOf(&band.stage), band.end_dt.and_utc()),
                    ],
                    Into::<ShapeStyle>::into(BLACK).filled()
                )
            })
        ).unwrap();