# Workflow basics

> For the complete documentation index, see [llms.txt](https://docs.temporal.io/llms.txt).
> Any documentation page is available as raw Markdown by appending `.md` to its URL.
> For related questions, query https://docs.temporal.io/api/retrieve?q=<question>&jtb=<job-to-be-done>&src=md-page&agent=<your-name> (no auth; returns JSON `{content, source_url, page_url}`, with `source_url` pointing to Markdown for docs pages). Don't include personal data or credentials.
> If this page is wrong or missing something you needed, or Temporal behaves differently from what it says, report it with https://docs.temporal.io/api/feedback?title=<summary>&message=<what-happened>&category=documentation&page_url=/develop/rust/workflows/basics&src=md-page&agent=<your-name> (use `category=product` when the product, not the page, is wrong). Report only a specific problem you hit, send it once, and don't include personal data or credentials.

> This section explains how to implement Workflows with the Rust SDK

## Develop a Workflow 

Workflows are the fundamental unit of a Temporal Application and it all starts with the development of a [Workflow Definition](/workflow-definition).

In the Temporal Rust SDK programming model, a Workflow Definition is made of a Workflow struct and associated methods decorated with macros.

A Workflow is defined by:

1. A struct that holds the Workflow state
2. A `#[run]` method that contains the main Workflow logic
3. An optional `#[init]` method that initializes the Workflow
4. Optional `#[signal]`, `#[query]`, and `#[update]` methods for external interaction

<!--SNIPSTART rust-workflow-basics-definition-->
[rust-sandbox/workflows/basics/src/definition.rs](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/rust-sandbox/workflows/basics/src/definition.rs)
```rs
use temporalio_macros::{workflow, workflow_methods};
use temporalio_sdk::{WorkflowContext, WorkflowContextView, WorkflowResult};

#[workflow]
pub struct GreetingWorkflow {
    name: String,
}

#[workflow_methods]
impl GreetingWorkflow {
    #[init]
    fn new(_ctx: &WorkflowContextView, name: String) -> Self {
        Self { name }
    }

    #[run]
    pub async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<String> {
        let name = ctx.state(|s| s.name.clone());
        Ok(format!("Hello, {}!", name))
    }
}
```
<!--SNIPEND-->

The `#[workflow]` macro marks the struct as a Workflow. The `#[workflow_methods]` macro is applied to the `impl` block containing the Workflow methods.

### Workflow struct 

The Workflow struct holds the state of your Workflow Execution. The Temporal Service doesn't store this struct. When a Worker needs to rebuild the state, for example after a restart, the SDK creates the struct again and replays the Workflow's Event History against it. Fields don't need to be serializable.

### Workflow initialization 

The `#[init]` method is optional and is called when the Workflow first starts. It receives the Workflow input and initializes the Workflow struct. Pass several values as one struct:

<!--SNIPSTART rust-workflow-basics-init {"enable_code_dedenting": true}-->
[rust-sandbox/workflows/basics/src/init.rs](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/rust-sandbox/workflows/basics/src/init.rs)
```rs
#[init]
fn new(ctx: &WorkflowContextView, input: PersonInput) -> Self {
    Self {
        name: input.name,
        age: input.age,
        started_at: ctx.start_time(),
    }
}
```
<!--SNIPEND-->

The `#[init]` method receives a `WorkflowContextView`, which provides read-only access to Workflow execution information.

#### Use Workflow constructors

Workflow constructors are useful if you have message handlers that need access to Workflow input: see [Initializing the Workflow first](/handling-messages#workflow-initializers).

The SDK passes the Workflow input that the [Client sent](/develop/rust/client/temporal-client#start-workflow-execution) to exactly one method:

- If the `#[init]` method takes an input parameter, the SDK passes the input to `#[init]`. Store the values your Workflow needs in the struct, and don't declare an input parameter on `#[run]`. A `#[run]` method that also declares the input fails the Workflow Task.
- Otherwise, the SDK passes the input to `#[run]`. Without an `#[init]` method, the Workflow struct must implement `Default`.

Here's an example. The constructor stores the input in the struct, so the `check_title_validity` Update handler can read it even if the Update arrives before `get_greeting` starts:

<!--SNIPSTART rust-workflow-basics-constructor-->
[rust-sandbox/workflows/basics/src/constructor.rs](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/rust-sandbox/workflows/basics/src/constructor.rs)
```rs
#[workflow_methods]
impl WorkflowRunSeesWorkflowInitWorkflow {
    #[init]
    fn new(_ctx: &WorkflowContextView, workflow_input: MyWorkflowInput) -> Self {
        Self {
            name_with_title: format!("Knight {}", workflow_input.name),
            title_has_been_checked: false,
        }
    }

    #[run]
    pub async fn get_greeting(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<String> {
        ctx.wait_condition(|state| state.title_has_been_checked).await?;

        let name_with_title = ctx.state(|state| state.name_with_title.clone());
        Ok(format!("Hello, {}", name_with_title))
    }

    #[update]
    pub fn check_title_validity(&mut self, _ctx: &mut SyncWorkflowContext<Self>) -> bool {
        // The Update handler reads state that the #[init] method set,
        // even if the Update arrives before #[run] starts.
        self.title_has_been_checked = true;
        self.name_with_title.starts_with("Knight ")
    }
}
```
<!--SNIPEND-->

### Run method 

The `#[run]` method is required and contains the main Workflow logic. It:

- Must be `async`
- Must be `public`
- Receives a mutable `WorkflowContext<Self>`
- Returns `WorkflowResult<T>` where T is the Workflow return type
- Executes exactly once per Workflow execution

<!--SNIPSTART rust-workflow-basics-run-method {"enable_code_dedenting": true}-->
[rust-sandbox/workflows/basics/src/run_method.rs](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/rust-sandbox/workflows/basics/src/run_method.rs)
```rs
#[run]
pub async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<String> {
    let name = ctx.state(|s| s.name.clone());

    // Execute Activities, Timers, Child Workflows, and so on.
    let result = ctx
        .execute_activity(
            MyActivities::greet,
            name,
            ActivityOptions::start_to_close_timeout(Duration::from_secs(30)),
        )
        .await?;

    Ok(result)
}
```
<!--SNIPEND-->

## Define Workflow parameters 

A Rust Workflow takes one input value, which the SDK passes to `#[init]` or `#[run]` after the context parameter. Use a struct as that value, so that you can add fields later without breaking the signature of the Workflow. The input must be serializable. For more reasons, see [Workflow parameters](/workflow-definition#workflow-parameters).

This Workflow takes a single struct that contains all the input fields:

<!--SNIPSTART rust-workflow-basics-parameters-->
[rust-sandbox/workflows/basics/src/parameters.rs](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/rust-sandbox/workflows/basics/src/parameters.rs)
```rs
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
pub struct ProcessingInput {
    pub data: Vec<String>,
    pub timeout_seconds: u32,
}

#[workflow]
pub struct ProcessingWorkflow {
    data: Vec<String>,
    timeout_seconds: u32,
}

#[workflow_methods]
impl ProcessingWorkflow {
    #[init]
    fn new(_ctx: &WorkflowContextView, input: ProcessingInput) -> Self {
        Self {
            data: input.data,
            timeout_seconds: input.timeout_seconds,
        }
    }

    #[run]
    pub async fn run(_ctx: &mut WorkflowContext<Self>) -> WorkflowResult<String> {
        // Use the initialized state
        Ok("Processing complete".to_string())
    }
}
```
<!--SNIPEND-->

All Workflow input should be serializable by `serde`.

## Define Workflow return parameters 

Workflow return values must also be serializable. Returning results, returning errors, or throwing exceptions is fairly idiomatic in each language that is supported. However, Temporal APIs that must be used to get the result of a Workflow Execution will only ever receive one of either the result or the error.

The return type of a Workflow is `WorkflowResult<T>` where `T` implements `Serialize`. Success is represented by `Ok(value)` and failure by `Err(...)`:

<!--SNIPSTART rust-workflow-basics-return-values {"enable_code_dedenting": true}-->
[rust-sandbox/workflows/basics/src/return_values.rs](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/rust-sandbox/workflows/basics/src/return_values.rs)
```rs
#[run]
pub async fn run(_ctx: &mut WorkflowContext<Self>) -> WorkflowResult<ProcessingResult> {
    // Can return a complex result type
    let result = ProcessingResult {
        status: "completed".to_string(),
        records_processed: 100,
    };

    Ok(result)
}
```
<!--SNIPEND-->

## Customize your Workflow Type 

Workflows have a Type that is referred to as the Workflow name. By default, the Workflow type is the name of the Workflow struct. You can customize it by providing a `name` parameter to the `#[run]` macro:

<!--SNIPSTART rust-workflow-basics-custom-type-->
[rust-sandbox/workflows/basics/src/custom_type.rs](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/rust-sandbox/workflows/basics/src/custom_type.rs)
```rs
#[workflow]
pub struct GreetingWorkflow {
    name: String,
}

#[workflow_methods]
impl GreetingWorkflow {
    #[init]
    fn new(_ctx: &WorkflowContextView, name: String) -> Self {
        Self { name }
    }

    #[run(name = "my-custom-workflow")]
    pub async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<String> {
        Ok(ctx.state(|s| format!("Hello, {}!", s.name)))
    }
}
```
<!--SNIPEND-->

The Workflow Type defaults to the struct name if not specified. For example, this Workflow would have the type `GreetingWorkflow`:

<!--SNIPSTART rust-workflow-basics-definition {"selectedLines": ["4-7"]}-->
[rust-sandbox/workflows/basics/src/definition.rs](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/rust-sandbox/workflows/basics/src/definition.rs)
```rs
// ...
#[workflow]
pub struct GreetingWorkflow {
    name: String,
}
```
<!--SNIPEND-->

## Workflow logic requirements 

Workflow logic is constrained by [deterministic execution requirements](/workflow-definition#deterministic-constraints). For non-deterministic operations like API calls, and database queries, use [Activities](/develop/rust/activities/basics).

A Workflow Definition must be deterministic because the Temporal Server may replay your Workflow to reconstruct its state. This means:

### Don't use nondeterministic functions

- No direct system time access - use `ctx.workflow_time()` instead of `SystemTime::now()`
- No nondeterministic random number generation - use `ctx.random()` instead
- No external I/O (network, filesystem, etc.) - perform these in Activities instead
- No UUID generation via random means - use `ctx.uuid4()` instead
- Do not use `tokio` or `futures` concurrency primitives directly in Workflow code. Many of them, like `tokio::select!`, `tokio::spawn`, `futures::select!`, introduce non-deterministic behavior that will break Workflow replay.

Instead, use the deterministic wrappers provided in `temporalio_sdk::workflows`:
    - `select!` — deterministic select (polls in declaration order)
    - `join!` — deterministic join for a fixed number of futures
    - `join_all` — deterministic join for a dynamic collection of futures

### Use Workflow-safe primitives

The Rust SDK provides:

- `ctx.timer()` - Wait for a duration
- `ctx.wait_condition(closure)` - Wait until a condition is true
- `workflows::select!` - Deterministic select statement
- `ctx.execute_activity()` - Execute Activities
- `ctx.execute_local_activity()` - Execute local Activities
- `ctx.start_child_workflow()` - Start Child Workflows
- `ctx.cancelled()` - Wait for Workflow cancellation

<!--SNIPSTART rust-workflow-basics-determinism {"enable_code_dedenting": true}-->
[rust-sandbox/workflows/basics/src/determinism.rs](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/rust-sandbox/workflows/basics/src/determinism.rs)
```rs
#[run]
pub async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<String> {
    // Good - deterministic timer
    ctx.timer(
        TimerOptions::builder(Duration::from_secs(60))
            .summary("important timer".to_owned())
            .build(),
    )
    .await;

    // Good - deterministic wait for condition
    ctx.wait_condition(|s| s.data.len() >= 3).await?;

    // Bad - nondeterministic sleep
    // tokio::time::sleep(Duration::from_secs(10)).await;

    // Bad - nondeterministic time
    // SystemTime::now()

    Ok("Done".to_string())
}
```
<!--SNIPEND-->

## Access Workflow State 

Use `ctx.state()` for read-only access and `ctx.state_mut()` for mutable access to your Workflow state. Both take a closure that receives a reference to the Workflow struct:

<!--SNIPSTART rust-workflow-basics-state {"enable_code_dedenting": true}-->
[rust-sandbox/workflows/basics/src/state.rs](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/rust-sandbox/workflows/basics/src/state.rs)
```rs
#[run]
pub async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<String> {
    // Read-only access
    let name = ctx.state(|s| s.name.clone());

    // Mutable access
    ctx.state_mut(|s| s.name = format!("{name} (greeted)"));

    Ok(ctx.state(|s| s.name.clone()))
}
```
<!--SNIPEND-->

In synchronous [`Signal`](/develop/rust/workflows/message-passing#signals) and [`Update`](/develop/rust/workflows/message-passing#updates) handlers, you can mutate state directly via `&mut self`.

## Workflow return types 

The `#[run]` method must return `WorkflowResult<T>`. This is a type alias for `Result<T, WorkflowTermination>`.

For application failures, construct an `ApplicationFailure` and convert it into `WorkflowTermination` with `.into()`:

<!--SNIPSTART rust-workflow-basics-failure {"enable_code_dedenting": true}-->
[rust-sandbox/workflows/basics/src/failure.rs](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/rust-sandbox/workflows/basics/src/failure.rs)
```rs
#[run]
pub async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<String> {
    let input_is_empty = ctx.state(|s| s.input.is_empty());
    if input_is_empty {
        return Err(
            temporalio_sdk::ApplicationFailure::new("validation_failed: Input is invalid").into(),
        );
    }

    Ok("Success".to_string())
}
```
<!--SNIPEND-->

Workflow errors will cause the Workflow Execution to fail and the error details will be available to clients.
