[Canonical page](<https://docs.dhtmlx.com/planning-engine/rust/api/core/results/task-slack/>)
[Full documentation index](<https://docs.dhtmlx.com/planning-engine/llms.txt>)


# TaskSlack

The total and free slack of one task. It isn't built by hand. Read it from [`Analytics::slack`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/results/analytics.md>), which is `Some` when the run asked for it with [`AnalyticsOptions::slack`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/analytics-options.md>). For how slack is computed and which tasks get an entry, see [Critical path and slack](<https://docs.dhtmlx.com/planning-engine/rust/scheduling/critical-path-and-slack.md>).

## Declaration

```rust
#[derive(Clone, Debug, PartialEq)]
pub struct TaskSlack {
    pub task: Id,
    pub total: f64,
    pub free: f64,
}
```

With the `serde` feature, it also implements `Serialize` and `Deserialize`.

## Fields

| Name | Type | Description |
|---|---|---|
| `task` | [`Id`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/id.md>) | Id of the task. |
| `total` | `f64` | Total slack: how far the task can slip before the project finish moves. Zero or negative on a critical task. |
| `free` | `f64` | Free slack: how far the task can slip before it pushes a successor. Never more than `total`, and equal to `total` when the task has no successors. |

Both values are in the configured [`TimeUnit`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/time-unit.md>) (`duration_unit` in [`SchedulingConfig`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/scheduling-config.md>)) and count working time on the task's calendar. A completed task (with `use_progress` and `progress == 1`) reports `0.0` for both.

## Serialization

With the `serde` feature, a `TaskSlack` serializes as:

```json
{ "task": "docs", "total": 2.0, "free": 2.0 }
```

## Example

Schedule two tasks that both wait for a third, then print each task's slack.

```rust
use plen_core::{
    schedule, AnalyticsOptions, DateTime, LinkData, LinkType, ProjectData, SolveOptions, TaskData,
};

fn task(id: &str, start: DateTime, duration: f64) -> TaskData {
    let mut task = TaskData::new(id);
    task.start_date = Some(start);
    task.duration = Some(duration);
    task
}

fn fs(id: &str, source: &str, target: &str) -> LinkData {
    LinkData {
        id: id.into(),
        source: source.into(),
        target: target.into(),
        link_type: LinkType::Fs,
        lag: 0.0,
        lag_unit: None,
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let monday = DateTime::from_ymd_hm(2026, 8, 10, 8, 0).ok_or("invalid date")?;
    let data = ProjectData {
        tasks: vec![
            task("design", monday, 2.0),
            task("build", monday, 3.0),
            task("docs", monday, 1.0),
        ],
        links: vec![fs("l1", "design", "build"), fs("l2", "design", "docs")],
        ..Default::default()
    };

    let options = SolveOptions {
        analytics: AnalyticsOptions {
            slack: true,
            ..Default::default()
        },
        ..Default::default()
    };
    let result = schedule(data, &options)?;

    let analytics = result.analytics.ok_or("analytics not requested")?;
    for s in analytics.slack.iter().flatten() {
        println!("{}: total {} free {}", s.task, s.total, s.free);
    }
    // design: total 0 free 0
    // build: total 0 free 0
    // docs: total 2 free 2
    Ok(())
}
```

## Related

- [`Analytics`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/results/analytics.md>) - holds the slack list
- [`Critical`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/results/critical.md>) - the critical path computed next to slack
- [`AnalyticsOptions`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/analytics-options.md>) - requests slack
- [Critical path and slack](<https://docs.dhtmlx.com/planning-engine/rust/scheduling/critical-path-and-slack.md>) - explains how slack is computed
