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


# LinkData

A dependency between two tasks: which end of the source task drives which end of the target task, and the working time between them. Links go in [`ProjectData::links`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/project-data.md>). Build one with a struct literal. `LinkData` has no constructor and no `Default`, so every field is set.

## Declaration

```rust
pub struct LinkData {
    pub id: Id,
    pub source: Id,
    pub target: Id,
    pub link_type: LinkType,
    pub lag: f64,
    pub lag_unit: Option<TimeUnit>,
}
```

## Fields

| Field | Type | Default | Description |
|---|---|---|---|
| `id` | [`Id`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/id.md>) | required | Link id, unique among the project's links. |
| `source` | [`Id`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/id.md>) | required | Id of the predecessor task. |
| `target` | [`Id`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/id.md>) | required | Id of the successor task. |
| `link_type` | [`LinkType`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/link-type.md>) | `LinkType::Fs` on the wire | Which end of `source` drives which end of `target`. |
| `lag` | `f64` | `0.0` on the wire | Working time between the two ends. Negative values are allowed. |
| `lag_unit` | `Option<`[`TimeUnit`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/time-unit.md>)`>` | `None` | Unit of `lag`. `None` uses `config.duration_unit`. |

### id

```rust
pub id: Id
```

The key that results use to refer to this link, for example in [`Note::DroppedLink`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/results/note.md>), [`Cycle`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/results/cycle.md>) and the critical path in [`Analytics`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/results/analytics.md>).

### source, target

```rust
pub source: Id
pub target: Id
```

Ids of the two tasks the link connects. An id that names no task, or names an unscheduled task, doesn't fail validation. The link is dropped and reported as a [`Note::DroppedLink`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/results/note.md>) with the reason. See [Links that get dropped](<https://docs.dhtmlx.com/planning-engine/rust/scheduling/links-constraints-summaries.md#links-that-get-dropped>) for every case.

### link_type

```rust
pub link_type: LinkType
```

`Fs` (finish-to-start), `Ss`, `Ff` or `Sf`. On the wire the field is named `type`, accepts numeric values and defaults to `"fs"` when omitted; see [`LinkType`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/link-type.md>).

### lag

```rust
pub lag: f64
```

Working time added between the source end and the target end, in `lag_unit`. A negative value lets the target overlap the source. Must be a finite number. Lag is an absolute amount; a percentage of the predecessor's duration isn't supported.

### lag_unit

```rust
pub lag_unit: Option<TimeUnit>
```

Unit for this link's `lag`. `None` uses [`SchedulingConfig::duration_unit`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/scheduling-config.md#duration_unit>).

:::note
A per-link `lag_unit` is a Planning Engine extension. dhtmlxGantt always reads lag in `config.duration_unit`, so a `lag_unit` equal to `config.duration_unit` matches gantt exactly; any other unit has no gantt counterpart.
:::

## Errors

The snapshot is validated when it's passed to [`schedule`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/schedule.md>) or [`Project::solve(Some(data))`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md#solve>). A link fails with [`Error::Input`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/errors/error.md>) when:

- its `id` repeats the id of an earlier link: path `links[i].id`
- its `lag` is `NaN` or infinite: path `links[i].lag`

[`Validation::Repair`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/validation.md>) repairs a repeated id instead of failing.

Links added or edited after the first solve go through [`LinkAdd`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/changes/link-add.md>) and [`LinkUpdate`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/changes/link-update.md>).

## Example

Task 2 starts when task 1 finishes, and task 3 starts four hours after task 2 starts, while the project counts durations in days:

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

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let start = DateTime::from_ymd_hm(2026, 4, 6, 8, 0).ok_or("invalid date")?;

    let data = ProjectData {
        tasks: vec![
            TaskData { start_date: Some(start), duration: Some(2.0), ..TaskData::new(1) },
            TaskData { start_date: Some(start), duration: Some(3.0), ..TaskData::new(2) },
            TaskData { start_date: Some(start), duration: Some(1.0), ..TaskData::new(3) },
        ],
        links: vec![
            LinkData {
                id: "a".into(),
                source: 1.into(),
                target: 2.into(),
                link_type: LinkType::Fs,
                lag: 0.0,
                lag_unit: None,
            },
            LinkData {
                id: "b".into(),
                source: 2.into(),
                target: 3.into(),
                link_type: LinkType::Ss,
                lag: 4.0,
                lag_unit: Some(TimeUnit::Hour),
            },
        ],
        ..Default::default()
    };

    let result = schedule(data, &SolveOptions::default())?;
    for update in &result.updates {
        println!("{update:?}");
    }
    Ok(())
}
```

For how link types, lag and dropped links shape the plan, see [Links, constraints and summaries](<https://docs.dhtmlx.com/planning-engine/rust/scheduling/links-constraints-summaries.md>).

## Related

- [`LinkType`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/link-type.md>) - which ends of the two tasks the link connects
- [`TaskData`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/task-data.md>) - the tasks a link connects
- [`LinkUpdate`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/changes/link-update.md>) - edits an existing link in a live project
- [Links, constraints and summaries](<https://docs.dhtmlx.com/planning-engine/rust/scheduling/links-constraints-summaries.md>) - how links, lag and dropped links shape the plan
- [Project data](<https://docs.dhtmlx.com/planning-engine/rust/scheduling/project-data.md>) - links in an input snapshot
