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


# Project::suggest_assignments

Proposes resources for requirement slots of a [`Project`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md>). It fills empty slots, repairs broken ones and rebalances valid ones, scoring each step like [`evaluate`](<https://docs.dhtmlx.com/planning-engine/rust/api/project-evaluate.md>). The project is never modified: the proposal comes back as a change set.

## Signature

```rust
pub fn suggest_assignments(
    &self,
    slots: &[Slot],
    options: &SuggestOptions,
) -> Result<SuggestResult, Error>
```

Takes `&self`. Synchronous and CPU-only.

## Parameters

| Name | Type | Description |
|---|---|---|
| `slots` | `&[`[`Slot`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/suggest/slot.md>)`]` | The slots to fill or rebalance. To build them from skills and requirements, use [`plen_assign::slots`](<https://docs.dhtmlx.com/planning-engine/rust/api/assign/slots.md>). To build your own, read the state with [`roster`](<https://docs.dhtmlx.com/planning-engine/rust/api/project-roster.md>). |
| `options` | `&`[`SuggestOptions`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/suggest/suggest-options.md>) | Targets, fixed assignments, penalty, passes and leveling stage |

## Behavior

- Greedy: slots are visited one at a time, and later slots build on earlier picks.
- It never removes an assignment. It adds one per filled slot and changes the resource of a repaired or rebalanced one.
- Commit the proposal with `apply(&result.changes)`, then `solve(None)`, or `level` with the same leveling options when they were set.

## Returns

A [`SuggestResult`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/suggest/suggest-result.md>) with the proposed changes and a step log.

## Errors

| Error | When |
|---|---|
| [`Error::Project(ProjectError::NoBaseline)`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/errors/project-error.md>) | The project is unsolved, including after `apply` |
| `Error::Project(ProjectError::StaleBaseline)` | `options.baseline_id` doesn't name the current baseline |
| [`Error::Input`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/errors/error.md>) | Invalid options (`new_resource_penalty`, `passes`, `targets[i]`, `fixed[i]`, `level`); a cyclic accepted state (`path: None`); an invalid slot (paths under `slots[i]`) |

Options are checked before slots. The accepted values are on the [`SuggestOptions`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/suggest/suggest-options.md>) and [`Slot`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/suggest/slot.md>) pages.

## Example

Fill an open position on task `C`, then commit the proposal.

```rust
use plen_core::{
    AssignmentData, DateTime, Project, ProjectData, ResourceData, Slot, SolveOptions,
    SuggestOptions, TaskData,
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let start = DateTime::from_ymd_hm(2026, 8, 10, 0, 0).ok_or("invalid date")?;
    let task = |id: &str| TaskData {
        start_date: Some(start),
        duration: Some(2.0),
        ..TaskData::new(id)
    };
    let resource = |id: &str| ResourceData { id: id.into(), capacity: None, calendar_id: None };
    let mut data = ProjectData {
        tasks: vec![task("A"), task("C")],
        resources: vec![resource("dev1"), resource("dev2")],
        assignments: vec![AssignmentData {
            id: Some("aA".into()),
            task: "A".into(),
            resource: "dev1".into(),
            value: 1.0,
            offset: None,
            duration: None,
        }],
        ..Default::default()
    };
    data.config.use_calendars = false;

    let mut project = Project::default();
    project.solve(Some(data), &SolveOptions::default())?;

    // Task C needs one person: dev1 or dev2.
    let slots = vec![Slot::new("sC", "C", vec!["dev1".into(), "dev2".into()])];
    let result = project.suggest_assignments(&slots, &SuggestOptions::default())?;
    for step in &result.steps {
        println!("{} -> {}", step.slot, step.to_resource); // sC -> dev2
    }

    let report = project.apply(&result.changes)?;
    if report.rejected.is_empty() {
        project.solve(None, &SolveOptions::default())?;
    }
    Ok(())
}
```

## Related

- [`Project`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md#suggest_assignments>) - the type and its other methods
- [`Slot`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/suggest/slot.md>) and [`SuggestOptions`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/suggest/suggest-options.md>) - the input
- [`SuggestResult`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/suggest/suggest-result.md>) - the returned proposal
- [Suggesting assignments](<https://docs.dhtmlx.com/planning-engine/rust/resources-and-assignment/suggesting-assignments.md>) - how picks are made, options and committing
- [Skills and Requirements](<https://docs.dhtmlx.com/planning-engine/rust/resources-and-assignment/skills-and-requirements.md>) - generating slots from skills
