# Workflow message passing

> 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/message-passing&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.

> Develop with Queries, Signals, and Updates with the Temporal Rust SDK.

A Workflow can act like a stateful web service that receives messages: Queries, Signals, and Updates.
The Workflow implementation defines these endpoints via handler methods that can react to incoming messages and return values.

Temporal Clients use messages to read Workflow state and control its execution.
See [Workflow message passing](/encyclopedia/workflow-message-passing) for a general overview of this topic. This page introduces these features for the Temporal Rust SDK.

## Write message handlers 

Follow these guidelines when writing your message handlers:

- Message handlers are defined as methods on your Workflow struct and registered with the Workflow runtime.
- The parameters and return values of handlers and the main Workflow function must be [serializable](/dataconversion).
- Prefer structs to multiple input parameters to allow for forward-compatible changes.

### Query handlers 

A [Query](/sending-messages#sending-queries) is a synchronous operation that retrieves state from a Workflow Execution:

<!--SNIPSTART rust-message-passing-workflow {"selectedLines": ["1-23", "35-93", "162"]}-->
[rust-sandbox/workflows/message-passing/src/workflows.rs](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/rust-sandbox/workflows/message-passing/src/workflows.rs)
```rs
use std::{collections::HashMap, time::Duration};

use temporalio_macros::{workflow, workflow_methods};
use temporalio_sdk::{
    ActivityOptions, SyncWorkflowContext, WorkflowContext, WorkflowContextView, WorkflowResult,
};

use crate::activities::GreetingActivities;

#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Language {
    Arabic,
    Chinese,
    English,
    French,
    Hindi,
    Spanish,
}

#[derive(serde::Serialize, serde::Deserialize)]
pub struct GetLanguagesInput {
    pub include_unsupported: bool,
}
// ...
#[workflow]
pub struct GreetingsWorkflow {
    greetings: HashMap<Language, String>,
    language: Language,
    approved_for_release: bool,
    approver_name: Option<String>,
    greeting_service_busy: bool,
}

#[workflow_methods]
impl GreetingsWorkflow {
    #[init]
    fn new(_ctx: &WorkflowContextView) -> Self {
        let greetings = HashMap::from([
            (Language::Chinese, "你好，世界".to_string()),
            (Language::English, "Hello, world".to_string()),
        ]);

        Self {
            greetings,
            language: Language::English,
            approved_for_release: false,
            approver_name: None,
            greeting_service_busy: false,
        }
    }

    #[run]
    pub async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<String> {
        // Wait for the approve Signal.
        ctx.wait_condition(|s| s.approved_for_release).await?;

        // Wait for running Signal and Update handlers to finish.
        ctx.wait_condition(|_| ctx.all_handlers_finished()).await?;

        Ok(ctx.state(|s| s.greetings[&s.language].clone()))
    }

    #[query]
    pub fn get_languages(
        &self,
        _ctx: &WorkflowContextView,
        input: GetLanguagesInput,
    ) -> Vec<Language> {
        if input.include_unsupported {
            vec![
                Language::Arabic,
                Language::Chinese,
                Language::English,
                Language::French,
                Language::Hindi,
                Language::Spanish,
            ]
        } else {
            let mut languages: Vec<Language> = self.greetings.keys().copied().collect();
            languages.sort_by_key(|language| format!("{language:?}"));
            languages
        }
    }
// ...
}
```
<!--SNIPEND-->

<!--SNIPSTART rust-message-passing-start {"enable_code_dedenting": true}-->
[rust-sandbox/workflows/message-passing/src/client.rs](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/rust-sandbox/workflows/message-passing/src/client.rs)
```rs
let wf_handle = client
    .start_workflow(
        GreetingsWorkflow::run,
        (),
        WorkflowStartOptions::new("my-task-queue", "greetings-workflow-10").build(),
    )
    .await?;
```
<!--SNIPEND-->

<!--SNIPSTART rust-message-passing-send-query {"enable_code_dedenting": true}-->
[rust-sandbox/workflows/message-passing/src/client.rs](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/rust-sandbox/workflows/message-passing/src/client.rs)
```rs
let supported_languages = wf_handle
    .query(
        GreetingsWorkflow::get_languages,
        GetLanguagesInput { include_unsupported: true },
        WorkflowQueryOptions::default(),
    )
    .await?;
```
<!--SNIPEND-->

- Query handlers can't mutate Workflow state.
- Query handlers can't perform async operations, like executing Activities.

### Signal handlers 

A [Signal](/sending-messages#sending-signals) is an asynchronous message sent to a running Workflow Execution to change its state and control its flow:

<!--SNIPSTART rust-message-passing-workflow {"selectedLines": ["25-28", "44-45", "95-99", "162"]}-->
[rust-sandbox/workflows/message-passing/src/workflows.rs](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/rust-sandbox/workflows/message-passing/src/workflows.rs)
```rs
// ...
#[derive(serde::Serialize, serde::Deserialize)]
pub struct ApproveInput {
    pub name: String,
}
// ...
#[workflow_methods]
impl GreetingsWorkflow {
// ...
    #[signal]
    pub fn approve(&mut self, _ctx: &mut SyncWorkflowContext<Self>, input: ApproveInput) {
        self.approved_for_release = true;
        self.approver_name = Some(input.name);
    }
// ...
}
```
<!--SNIPEND-->

<!--SNIPSTART rust-message-passing-send-signal {"enable_code_dedenting": true}-->
[rust-sandbox/workflows/message-passing/src/client.rs](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/rust-sandbox/workflows/message-passing/src/client.rs)
```rs
wf_handle
    .signal(
        GreetingsWorkflow::approve,
        ApproveInput { name: "Ziggy".to_string() },
        WorkflowSignalOptions::default(),
    )
    .await?;
```
<!--SNIPEND-->

- Signal handlers do not return values.
- Signal handlers can be `async fn` and use Activities, Timers, and Child Workflows. See [Async handlers](#async-handlers).

### Update handlers and validators 

An [Update](/sending-messages#sending-updates) is a trackable synchronous request sent to a running Workflow Execution. It can change the Workflow state, control its flow, and return a result. The sender must wait until the Worker accepts or rejects the Update. The sender may wait further to receive a returned value or an exception if something goes wrong:

<!--SNIPSTART rust-message-passing-workflow {"selectedLines": ["30-33", "44-45", "101-124", "162"]}-->
[rust-sandbox/workflows/message-passing/src/workflows.rs](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/rust-sandbox/workflows/message-passing/src/workflows.rs)
```rs
// ...
#[derive(serde::Serialize, serde::Deserialize)]
pub struct SetLanguageInput {
    pub language: Language,
}
// ...
#[workflow_methods]
impl GreetingsWorkflow {
// ...
    #[update]
    pub fn set_language(
        &mut self,
        _ctx: &mut SyncWorkflowContext<Self>,
        input: SetLanguageInput,
    ) -> Language {
        let previous_language = self.language;
        self.language = input.language;

        previous_language
    }

    #[update_validator(set_language)]
    fn validate_set_language(
        &self,
        _ctx: &WorkflowContextView,
        input: &SetLanguageInput,
    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        if !self.greetings.contains_key(&input.language) {
            Err("Not a valid language".into())
        } else {
            Ok(())
        }
    }
// ...
}
```
<!--SNIPEND-->

- About validators:
    - Use validators to reject an Update before it is written to History. Validators are always optional. If you don't need to reject Updates, you can skip them.
    - If you choose to create validators for your Updates, they will reject Updates before they're applied.
- Accepting and rejecting Updates with validators:
    - To reject an Update, return an `Err` from the validator.
    - Without a validator, Updates are always accepted.
- Validators and Event History:
    - The `WorkflowExecutionUpdateAccepted` event is written into the History whether the acceptance was automatic or programmatic.
    - When a validator returns an error, the Update is rejected and `WorkflowExecutionUpdateAccepted` won't be added to the Event History. The caller receives the error.
- Update and Signal handlers can be async, letting you use Activities, Child Workflows, and more. See [Async handlers](#async-handlers) for safe usage guidelines.

## Send messages 

To send Queries, Signals, or Updates, use a Workflow handle from the client:

<!--SNIPSTART rust-message-passing-start {"enable_code_dedenting": true}-->
[rust-sandbox/workflows/message-passing/src/client.rs](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/rust-sandbox/workflows/message-passing/src/client.rs)
```rs
let wf_handle = client
    .start_workflow(
        GreetingsWorkflow::run,
        (),
        WorkflowStartOptions::new("my-task-queue", "greetings-workflow-10").build(),
    )
    .await?;
```
<!--SNIPEND-->

### Send a Query 

<!--SNIPSTART rust-message-passing-send-query {"enable_code_dedenting": true}-->
[rust-sandbox/workflows/message-passing/src/client.rs](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/rust-sandbox/workflows/message-passing/src/client.rs)
```rs
let supported_languages = wf_handle
    .query(
        GreetingsWorkflow::get_languages,
        GetLanguagesInput { include_unsupported: true },
        WorkflowQueryOptions::default(),
    )
    .await?;
```
<!--SNIPEND-->

- Sending a Query doesn’t add events to a Workflow's Event History.
- You can send Queries to closed Workflow Executions within a Namespace's Workflow retention period. This includes Workflows that have completed, failed, or timed out. Querying terminated Workflows is not safe and, therefore, not supported.
- A Worker must be online and polling the Task Queue to process a Query.

### Send a Signal 

You can send a Signal to a Workflow Execution from a Temporal Client or from another Workflow Execution. However, you can only send Signals to Workflow Executions that haven’t closed.

#### From a Client

<!--SNIPSTART rust-message-passing-send-signal {"enable_code_dedenting": true}-->
[rust-sandbox/workflows/message-passing/src/client.rs](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/rust-sandbox/workflows/message-passing/src/client.rs)
```rs
wf_handle
    .signal(
        GreetingsWorkflow::approve,
        ApproveInput { name: "Ziggy".to_string() },
        WorkflowSignalOptions::default(),
    )
    .await?;
```
<!--SNIPEND-->

- The call returns when the server accepts the Signal; it does not wait for the Signal to be delivered to the Workflow Execution.

#### From a Workflow

<!--SNIPSTART rust-message-passing-signal-from-workflow {"enable_code_dedenting": true}-->
[rust-sandbox/workflows/message-passing/src/approver.rs](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/rust-sandbox/workflows/message-passing/src/approver.rs)
```rs
// Pass a Run ID instead of None to target a specific run.
ctx.external_workflow(workflow_id, None)
    .signal(
        GreetingsWorkflow::approve,
        ApproveInput { name: "Ziggy".to_string() },
        SignalWorkflowOptions::default(),
    )
    .await?;
```
<!--SNIPEND-->

### Signal-With-Start 

<!--SNIPSTART rust-message-passing-signal-with-start {"enable_code_dedenting": true}-->
[rust-sandbox/workflows/message-passing/src/client.rs](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/rust-sandbox/workflows/message-passing/src/client.rs)
```rs
let wf_handle = client
    .signal_with_start_workflow(
        GreetingsWorkflow::run,
        (),
        GreetingsWorkflow::approve,
        ApproveInput { name: "Ziggy".to_string() },
        WorkflowStartOptions::new("my-task-queue", "greetings-workflow-11").build(),
    )
    .await?;
```
<!--SNIPEND-->

### Send an Update 

An Update is a synchronous, blocking call that can change Workflow state, control its flow, and return a result.

A client sending an Update must wait until the Server delivers the Update to a Worker. Workers must be available and responsive. If you need a response as soon as the Server receives the request, use a Signal instead. You can't send Updates directly from one Workflow to another. If you need to send Updates across Workflows, like to Child Workflows, use an Activity.

- `WorkflowExecutionUpdateAccepted` is added to the Event History when the Worker confirms that the Update passed validation.
- `WorkflowExecutionUpdateCompleted` is added to the Event History when the Worker confirms that the Update has finished.

To send an Update to a Workflow Execution, you can call `execute_update` and wait for the Update to complete.

This code fetches an Update result:

<!--SNIPSTART rust-message-passing-execute-update {"enable_code_dedenting": true}-->
[rust-sandbox/workflows/message-passing/src/client.rs](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/rust-sandbox/workflows/message-passing/src/client.rs)
```rs
let previous_language = wf_handle
    .execute_update(
        GreetingsWorkflow::set_language,
        SetLanguageInput { language: Language::Chinese },
        WorkflowExecuteUpdateOptions::default(),
    )
    .await?;
```
<!--SNIPEND-->

#### Start an Update

You can also send `start_update` to receive an `UpdateHandle` as soon as the Update is accepted.

- Use this `UpdateHandle` later to fetch your results.
- Async Update handlers normally perform long-running asynchronous operations, such as executing an Activity.
- `start_update` only waits until the Worker has accepted or rejected the Update, not until all asynchronous operations are complete.

For example:

<!--SNIPSTART rust-message-passing-start-update {"enable_code_dedenting": true}-->
[rust-sandbox/workflows/message-passing/src/client.rs](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/rust-sandbox/workflows/message-passing/src/client.rs)
```rs
let update_handle = wf_handle
    .start_update(
        GreetingsWorkflow::set_language_activity,
        SetLanguageInput { language: Language::French },
        WorkflowStartUpdateOptions::default(),
    )
    .await?;

// Wait for the Update's Activity to finish and fetch the result.
let previous_language_from_activity_update =
    update_handle.get_result(RpcOptions::default()).await?;
```
<!--SNIPEND-->

- Updates are synchronous and return results.
- Worker must accept the Update before it proceeds.

#### Update-With-Start

Use `execute_update_with_start_workflow` to start a Workflow and send it an Update in one call.
You must choose a Workflow ID conflict policy.
With `WorkflowIdConflictPolicy::UseExisting`, the Update goes to the running Workflow if one already has this Workflow ID:

<!--SNIPSTART rust-message-passing-update-with-start {"enable_code_dedenting": true}-->
[rust-sandbox/workflows/message-passing/src/client.rs](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/rust-sandbox/workflows/message-passing/src/client.rs)
```rs
let previous_language = client
    .execute_update_with_start_workflow(
        GreetingsWorkflow::run,
        (),
        GreetingsWorkflow::set_language,
        SetLanguageInput { language: Language::Chinese },
        WorkflowUpdateWithStartOptions::new(
            "my-task-queue",
            "greetings-workflow-12",
            WorkflowIdConflictPolicy::UseExisting,
        )
        .build(),
    )
    .await?;
```
<!--SNIPEND-->

## Message handler patterns 

### Async handlers 

Signal and Update handlers can be `async fn` as well as `fn`. Using `async fn` allows you to use await with Activities, Child Workflows, Timers, and so on. This expands the possibilities for what can be done by a handler, but it also means that handler executions and your main Workflow method are all running concurrently, with switching occurring between them at await calls.

To use `async fn` handlers safely, understand what can go wrong. See [Workflow message passing](/encyclopedia/workflow-message-passing) for guidance on safe usage of async Signal and Update handlers, the Safe message handlers sample and the sections below.

The following code executes an Activity that makes a network call to a remote service:

<!--SNIPSTART rust-message-passing-activity-->
[rust-sandbox/workflows/message-passing/src/activities.rs](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/rust-sandbox/workflows/message-passing/src/activities.rs)
```rs
use std::{collections::HashMap, time::Duration};

use temporalio_macros::activities;
use temporalio_sdk::activities::{ActivityContext, ActivityError};
use tokio::time::sleep;

use crate::workflows::Language;

pub struct GreetingActivities;

#[activities]
impl GreetingActivities {
    #[activity]
    pub async fn call_greeting_service(
        _ctx: ActivityContext,
        to_language: Language,
    ) -> Result<Option<String>, ActivityError> {
        // Pretend that we are calling a remote service.
        sleep(Duration::from_millis(200)).await;

        let greetings = HashMap::from([
            (Language::Arabic, "مرحبا بالعالم".to_string()),
            (Language::Chinese, "你好，世界".to_string()),
            (Language::English, "Hello, world".to_string()),
            (Language::French, "Bonjour, monde".to_string()),
            (Language::Hindi, "नमस्ते दुनिया".to_string()),
            (Language::Spanish, "Hola mundo".to_string()),
        ]);

        Ok(greetings.get(&to_language).cloned())
    }
}
```
<!--SNIPEND-->

After updating the code to use an `async fn`, your Update handler can schedule an Activity and await the result. Although an `async fn` Signal handler can also execute an Activity, using an Update handler allows the client to receive a result or error once the Activity completes.

This lets your client track the progress of asynchronous work performed by the Update's Activities, Child Workflows, and so on. Here's how you could start that Activity from within your Workflow. The `greeting_service_busy` flag lets one handler call the greeting service at a time:

<!--SNIPSTART rust-message-passing-workflow {"selectedLines": ["44-45", "126-162"]}-->
[rust-sandbox/workflows/message-passing/src/workflows.rs](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/rust-sandbox/workflows/message-passing/src/workflows.rs)
```rs
// ...
#[workflow_methods]
impl GreetingsWorkflow {
// ...
    #[update]
    pub async fn set_language_activity(
        ctx: &mut WorkflowContext<Self>,
        input: SetLanguageInput,
    ) -> Result<Language, Box<dyn std::error::Error + Send + Sync>> {
        let language = input.language;
        let needs_greeting = ctx.state(|s| !s.greetings.contains_key(&language));

        if needs_greeting {
            // Let one handler call the greeting service at a time.
            ctx.wait_condition(|s| !s.greeting_service_busy).await?;
            ctx.state_mut(|s| s.greeting_service_busy = true);

            let greeting = ctx
                .execute_activity(
                    GreetingActivities::call_greeting_service,
                    language,
                    ActivityOptions::start_to_close_timeout(Duration::from_secs(10)),
                )
                .await;

            ctx.state_mut(|s| s.greeting_service_busy = false);

            let Some(greeting) = greeting? else {
                return Err("The greeting service doesn't support this language".into());
            };
            ctx.state_mut(|s| {
                s.greetings.insert(language, greeting);
            });
        }

        let previous_language = ctx.state(|s| s.language);
        ctx.state_mut(|s| s.language = language);

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

### Add wait conditions to block

Sometimes, async Signal or Update handlers need to meet certain conditions before they should continue. You can use `ctx.wait_condition` to prevent the code from proceeding until a condition is true. You specify the condition by passing a function that returns a boolean and you can optionally set a timeout. This is an important feature that helps you control your handler logic.

Here are three important use cases for `ctx.wait_condition`:

- Wait for a Signal or Update to arrive.
- Wait in a handler until it's appropriate to continue.
- Wait in the main Workflow until all active handlers have finished.

It's common to use `ctx.wait_condition` to wait for a particular Signal or Update to be sent by a client. This Workflow waits for the `approve` Signal:

<!--SNIPSTART rust-message-passing-workflow {"selectedLines": ["44-45", "62-71", "162"]}-->
[rust-sandbox/workflows/message-passing/src/workflows.rs](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/rust-sandbox/workflows/message-passing/src/workflows.rs)
```rs
// ...
#[workflow_methods]
impl GreetingsWorkflow {
// ...
    #[run]
    pub async fn run(ctx: &mut WorkflowContext<Self>) -> WorkflowResult<String> {
        // Wait for the approve Signal.
        ctx.wait_condition(|s| s.approved_for_release).await?;

        // Wait for running Signal and Update handlers to finish.
        ctx.wait_condition(|_| ctx.all_handlers_finished()).await?;

        Ok(ctx.state(|s| s.greetings[&s.language].clone()))
    }
// ...
}
```
<!--SNIPEND-->

### Ensure handlers finish before completion

Workflow wait conditions can ensure your handler completes before a Workflow finishes. When your Workflow uses async Signal or Update handlers, your main Workflow method can return or continue-as-new while a handler is still waiting on an async task, such as an Activity result.

The Workflow completing may interrupt the handler before it finishes crucial work and cause client errors when trying retrieve Update results. Use `ctx.wait_condition` with `ctx.all_handlers_finished()` to address this problem and allow your Workflow to end smoothly.
The `run` method in the previous example does this before it returns.
