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


# Cycle

One simple directed dependency cycle in the project. You don't build it yourself. Read it from [`ScheduleResult::cycles`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/results/schedule-result.md#cycles>), which holds one `Cycle` per unique cycle. A cycle is data in the result, not an error. For what a cyclic run does to dates and updates, see [`ScheduleResult`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/results/schedule-result.md>).

## Declaration

```rust
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Cycle {
    pub tasks: Vec<Id>,
    pub links: Vec<Id>,
}
```

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

## Fields

| Name | Type | Description |
|---|---|---|
| `tasks` | `Vec<`[`Id`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/id.md>)`>` | Ids of the tasks on the cycle, in the order the cycle passes through them. |
| `links` | `Vec<`[`Id`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/id.md>)`>` | Ids of the links that form the cycle, in path order. Each link id appears once. A cycle closed through summary containment rather than a link has fewer links than tasks. |

Each rotation of a cycle is reported once. Two cycles over the same tasks through different parallel links are separate entries. Fixed and completed tasks are included.

## Serialization

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

```json
{ "tasks": ["a", "b"], "links": ["ab", "ba"] }
```

## Example

Link two tasks in both directions and print the cycle.

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

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

    let mut a = TaskData::new("a");
    a.start_date = Some(monday);
    a.duration = Some(1.0);

    let mut b = TaskData::new("b");
    b.start_date = Some(monday);
    b.duration = Some(1.0);

    let link = |id: &str, source: &str, target: &str| LinkData {
        id: id.into(),
        source: source.into(),
        target: target.into(),
        link_type: LinkType::Fs,
        lag: 0.0,
        lag_unit: None,
    };

    let data = ProjectData {
        tasks: vec![a, b],
        links: vec![link("ab", "a", "b"), link("ba", "b", "a")],
        ..Default::default()
    };

    let result = schedule(data, &SolveOptions::default())?;

    for cycle in &result.cycles {
        let tasks: Vec<String> = cycle.tasks.iter().map(|id| id.to_string()).collect();
        let links: Vec<String> = cycle.links.iter().map(|id| id.to_string()).collect();
        println!("tasks: {}; links: {}", tasks.join(" -> "), links.join(", "));
        // tasks: a -> b; links: ab, ba
    }
    Ok(())
}
```

## Related

- [`ScheduleResult`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/results/schedule-result.md#cycles>) - lists the cycles of a run
- [`AnalyticsStatus`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/results/analytics-status.md>) - reports `Invalid` analytics on a cyclic project
- [`LinkData`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/link-data.md>) - the input links a cycle's `links` refer to
- [Links, constraints and summaries](<https://docs.dhtmlx.com/planning-engine/rust/scheduling/links-constraints-summaries.md#cycles>) - reports and breaks a cycle
- [Reading results](<https://docs.dhtmlx.com/planning-engine/rust/scheduling/reading-results.md#cycles-and-notes>) - handles cycles in a result
