Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions content/en/_index.html
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ <h4 class="section-text-bold">
</h4>
<p class="section-text">
Fission is extensible to any programming language (Python, NodeJS,
Go, C#, PHP are supported today). It abstracts away containers by
default, but you can build your own containers if you need to.
Go, Rust, C#, PHP are supported today). It abstracts away containers
by default, but you can build your own containers if you need to.
</p>
</div>
</div>
Expand Down Expand Up @@ -238,8 +238,8 @@ <h4 class="section-text-bold">Wide Language Support</h4>
</div>
<p class="section-text">
Fission is extensible to any programming language of your choice.
Python, NodeJS, Go, C#, PHP are supported today, but you can build
your own custom containers if you need to.
Python, NodeJS, Go, Rust, C#, PHP are supported today, but you can
build your own custom containers if you need to.
</p>
</div>
</div>
Expand Down
1 change: 1 addition & 0 deletions content/en/docs/usage/languages/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Each environment has a runtime image (`*-env`) and, where dependency building is
| Node.js | `ghcr.io/fission/node-env` | `ghcr.io/fission/node-builder` | [Node.js]({{% ref "nodejs.md" %}}) |
| Python | `ghcr.io/fission/python-env` | `ghcr.io/fission/python-builder` | [Python]({{% ref "python.md" %}}) |
| Go | `ghcr.io/fission/go-env` | `ghcr.io/fission/go-builder` | [Go]({{% ref "go.md" %}}) |
| Rust | `ghcr.io/fission/rust-env` | `ghcr.io/fission/rust-builder` | [Rust]({{% ref "rust.md" %}}) |
| Java (JVM) | `ghcr.io/fission/jvm-env` | `ghcr.io/fission/jvm-builder` | [Java]({{% ref "java.md" %}}) |
| Ruby | `ghcr.io/fission/ruby-env` | `ghcr.io/fission/ruby-builder` | [environments repo](https://github.com/fission/environments/tree/master/ruby) |
| PHP | `ghcr.io/fission/php-env` | `ghcr.io/fission/php-builder` | [environments repo](https://github.com/fission/environments/tree/master/php7) |
Expand Down
321 changes: 321 additions & 0 deletions content/en/docs/usage/languages/rust.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,321 @@
---
title: "Rust Functions"
description: "Writing Rust functions with fission"
weight: 10
---

Fission supports Rust as a first-class function language.
Rust functions are compiled by the builder into native server binaries built on [axum](https://docs.rs/axum) and [tokio](https://tokio.rs), so invocations run at native speed with no per-request process startup.

In this usage guide we'll cover how to use this environment, write functions, and work with dependencies.

### Before you start

We'll assume you have Fission and Kubernetes set up.
If not, head over to the [install guide]({{% ref "../../installation/_index.en.md" %}}).
Verify your Fission setup with:

```bash
fission version
```

### Add the Rust environment to your cluster

Rust is a compiled language, so source code must be compiled before it runs.
The builder manager inside Fission does this automatically whenever a Rust function or package is created: the Rust builder converts a source package into a deployable native binary.

```bash
fission environment create --name rust \
--image ghcr.io/fission/rust-env \
--builder ghcr.io/fission/rust-builder
```

#### Rust environment image list

| Image | Builder Image |
|-------|---------------|
| [ghcr.io/fission/rust-env](https://github.com/fission/environments/pkgs/container/rust-env/versions?filters%5Bversion_type%5D=tagged) | [ghcr.io/fission/rust-builder](https://github.com/fission/environments/pkgs/container/rust-builder/versions?filters%5Bversion_type%5D=tagged) |

### Write a simple function in Rust

A single-file function is one `.rs` file that defines `pub async fn handler`.
Any [axum handler](https://docs.rs/axum/latest/axum/handler/index.html) signature works, so you can use extractors, `Json`, headers, and so on.

Here is a hello world example (`hello.rs`):

```rust
use fission_rust::IntoResponse;

pub async fn handler() -> impl IntoResponse {
"Hello, World!\n"
}
```

Create and test the function:

```bash
fission fn create --name helloworld --env rust --src hello.rs
```

Before accessing the function, make sure its package build has succeeded:

```bash
fission pkg info --name <pkg-name>
```

Now test it:

```bash
$ fission fn test --name helloworld
Hello, World!
```

{{% notice info %}}
See [here]({{% ref "../triggers/_index.md" %}}) for how to set up different triggers for a Rust function.
{{% /notice %}}

Single-file functions may use the crates pre-baked into the builder: `fission-rust`, `axum`, `tokio`, `serde`, and `serde_json`.
For other dependencies, use a [Cargo project](#working-with-dependencies-cargo-projects).

### HTTP requests and HTTP responses

The function receives every request routed to it; axum extractors give you typed access to all parts of the request.

#### Accessing HTTP Requests

##### Headers

```rust
use fission_rust::IntoResponse;
use fission_rust::axum::http::HeaderMap;

pub async fn handler(headers: HeaderMap) -> impl IntoResponse {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[LanguageTool] reported by reviewdog 🐶
It appears that a white space is missing. (SPACE_BEFORE_PARENTHESIS[1])
Suggestions: (
URL: https://languagetool.org/insights/post/punctuation-guide/#what-are-parentheses
Rule: https://community.languagetool.org/rule/show/SPACE_BEFORE_PARENTHESIS?lang=en-US&subId=1
Category: TYPOGRAPHY

let v = headers
.get("x-my-header")
.and_then(|v| v.to_str().ok())
.unwrap_or("not set");
format!("x-my-header: {v}\n")
}
```

Create an HTTP trigger and call it:

```bash
fission httptrigger create --method GET --url "/<url>" --function <fn-name>
```

```bash
$ curl http://$FISSION_ROUTER/<url> -H 'X-My-Header: foo'
x-my-header: foo
```

##### Query string

```rust
use std::collections::HashMap;
use fission_rust::IntoResponse;
use fission_rust::axum::extract::Query;

pub async fn handler(Query(params): Query<HashMap<String, String>>) -> impl IntoResponse {
params.get("key-name").cloned().unwrap_or_default()
}
```

```bash
$ curl "http://$FISSION_ROUTER/<url>?key-name=123"
123
```

##### Request Body

###### Plain text

```rust
use fission_rust::IntoResponse;

pub async fn handler(body: String) -> impl IntoResponse {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[LanguageTool] reported by reviewdog 🐶
It appears that a white space is missing. (SPACE_BEFORE_PARENTHESIS[1])
Suggestions: (
URL: https://languagetool.org/insights/post/punctuation-guide/#what-are-parentheses
Rule: https://community.languagetool.org/rule/show/SPACE_BEFORE_PARENTHESIS?lang=en-US&subId=1
Category: TYPOGRAPHY

body
}
```

```bash
$ curl -X POST http://$FISSION_ROUTER/<url> -d foobar

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[LanguageTool] reported by reviewdog 🐶
Possible typo: you repeated a word (ENGLISH_WORD_REPEAT_RULE)
Suggestions: foobar
Rule: https://community.languagetool.org/rule/show/ENGLISH_WORD_REPEAT_RULE?lang=en-US
Category: MISC

foobar
```

###### JSON

```rust
use fission_rust::IntoResponse;
use fission_rust::axum::Json;
use serde::{Deserialize, Serialize};

#[derive(Deserialize, Serialize)]
pub struct Msg {
content: String,
}

pub async fn handler(Json(msg): Json<Msg>) -> impl IntoResponse {
Json(msg)
}
```

```bash
$ curl -X POST http://$FISSION_ROUTER/<url> \
-H 'Content-Type: application/json' -d '{"content": "foobar"}'
{"content":"foobar"}
```

#### Controlling HTTP Responses

##### Setting response headers and status codes

Return a tuple of status, headers, and body — axum converts it into a response:

```rust
use fission_rust::IntoResponse;
use fission_rust::axum::http::StatusCode;

pub async fn handler() -> impl IntoResponse {
(
StatusCode::CREATED,
[("x-request-handled-by", "fission-rust")],
"created\n",
)
}
```

```bash
$ curl -i http://$FISSION_ROUTER/<url>
HTTP/1.1 201 Created
x-request-handled-by: fission-rust
...
```

Handler panics are caught by the runtime: the request returns HTTP 500 and the function keeps serving.

### Multiple module files

A source archive may contain a `handler.rs` plus extra module files.
Each file becomes a crate-root module, so `handler.rs` can reach a sibling `util.rs` as `crate::util`:

```rust
// util.rs
pub fn greeting() -> String {
"Hello from a sibling module!\n".to_string()
}
```

```rust
// handler.rs
use fission_rust::IntoResponse;

pub async fn handler() -> impl IntoResponse {
crate::util::greeting()
}
```

```bash
zip -r function.zip handler.rs util.rs
fission pkg create --name multimod --env rust --src function.zip
```

### Working with dependencies (Cargo projects)

For third-party crates, supply a full Cargo binary crate as the source package.
The only contract: **the binary must serve HTTP on `127.0.0.1:$FISSION_RUNTIME_PORT`**.

The easiest way is the `fission-rust` SDK:

```toml
# Cargo.toml
[package]
name = "echo-example"
version = "0.1.0"
edition = "2024"

[dependencies]
fission-rust = { git = "https://github.com/fission/environments", rev = "<commit-sha>" }
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
serde_json = "1"
```

Pin the dependency to a commit with `rev` for reproducible builds.
Without `rev` the git dependency tracks `master` and can change under you between builds; pinning a `rev` keeps every build resolving the same source.
The builder pod fetches the crate over the network at build time, so the builder needs egress to GitHub — the same as for any crates.io dependency.

```rust
// src/main.rs
use fission_rust::IntoResponse;
use fission_rust::axum::Json;
use serde_json::Value;

async fn handler(body: String) -> impl IntoResponse {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[LanguageTool] reported by reviewdog 🐶
It appears that a white space is missing. (SPACE_BEFORE_PARENTHESIS[1])
Suggestions: (
URL: https://languagetool.org/insights/post/punctuation-guide/#what-are-parentheses
Rule: https://community.languagetool.org/rule/show/SPACE_BEFORE_PARENTHESIS?lang=en-US&subId=1
Category: TYPOGRAPHY

let value: Value = serde_json::from_str(&body)
.unwrap_or_else(|_| Value::String(body));
Json(value)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[LanguageTool] reported by reviewdog 🐶
It appears that a white space is missing. (SPACE_BEFORE_PARENTHESIS[1])
Suggestions: (
URL: https://languagetool.org/insights/post/punctuation-guide/#what-are-parentheses
Rule: https://community.languagetool.org/rule/show/SPACE_BEFORE_PARENTHESIS?lang=en-US&subId=1
Category: TYPOGRAPHY

}

#[tokio::main]
async fn main() {
fission_rust::serve(handler).await;
}
```

Archive and create the package as usual:

```bash
$ zip -r echo.zip Cargo.toml src
$ fission pkg create --name echo --env rust --src echo.zip
$ fission fn create --name echo --env rust --pkg echo
```

#### Bring your own framework

Because the contract is just "serve HTTP on `$FISSION_RUNTIME_PORT`", any Rust web framework works — actix-web, rocket, warp, or raw hyper:

```rust
// src/main.rs — plain axum without the SDK
use axum::{Router, routing::get};

#[tokio::main]
async fn main() {
let port: u16 = std::env::var("FISSION_RUNTIME_PORT")
.ok().and_then(|p| p.parse().ok()).unwrap_or(8889);
let app = Router::new().route("/", get(|| async { "Hello!\n" }));
let listener = tokio::net::TcpListener::bind(("127.0.0.1", port)).await.unwrap();
axum::serve(listener, app).await.unwrap();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[LanguageTool] reported by reviewdog 🐶
It appears that a white space is missing. (SPACE_BEFORE_PARENTHESIS[1])
Suggestions: (
URL: https://languagetool.org/insights/post/punctuation-guide/#what-are-parentheses
Rule: https://community.languagetool.org/rule/show/SPACE_BEFORE_PARENTHESIS?lang=en-US&subId=1
Category: TYPOGRAPHY

}
```

#### Multiple binaries and entrypoints

If the project defines several `[[bin]]` targets, the builder deploys all of them and the function's `--entrypoint` selects one by name:

```bash
fission fn create --name myfn --env rust --pkg mypkg --entrypoint <binary-name>
```

A project with a single binary needs no entrypoint — it is always deployed under the default name `handler`.

### How it works

The runtime image runs a small supervisor that implements the Fission environment interface.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[LanguageTool] reported by reviewdog 🐶
A comma is probably missing here. (MISSING_COMMA_AFTER_INTRODUCTORY_PHRASE[1])
Suggestions: specialization,
URL: http://englishplus.com/grammar/00000074.htm
Rule: https://community.languagetool.org/rule/show/MISSING_COMMA_AFTER_INTRODUCTORY_PHRASE?lang=en-US&subId=1
Category: PUNCTUATION

At specialization it starts your compiled function binary **once** and then reverse-proxies all requests to it over a pooled local connection, so steady-state overhead is a single localhost hop.
If the function process exits, the pod is replaced automatically.

### Modifying/Rebuilding the environment images

Refer to the [Rust environment guide](https://github.com/fission/environments/blob/master/rust/README.md) to learn about rebuilding the environment images.

### Resource usage

Like any function, you can bound a Rust function's resources:

```bash
fission fn create --name echo --env rust --pkg echo \
--mincpu 20 --maxcpu 100 --minmemory 64 --maxmemory 128
```

Compiled Rust functions are typically small (a hello world binary is around 1 MB) and have low memory floors, so they are a good fit for tight resource limits.
Use `kubectl top pod -l functionName=<name>` while benchmarking to find the right values.
11 changes: 11 additions & 0 deletions static/data/environments.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@
}
]
},
{
"name": "Rust",
"logo": "/images/lang-logo/rust-logo.svg",
"repo": "https://github.com/fission/environments/tree/master/rust",
"images": [
{
"main": "rust-env",
"builder": "rust-builder"
}
]
},
{
"name": "Python",
"logo": "/images/lang-logo/python-logo.svg",
Expand Down
1 change: 1 addition & 0 deletions static/images/lang-logo/rust-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions tools/environments.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
'jvm-env': 'Java',
'jvm-jersey-env-25': 'Java (JVM-Jersey)',
'ruby-env': 'Ruby',
'rust-env': 'Rust',
'python-env': 'Python',
'python-fastapi-env': 'Python (FastAPI)',
'binary-env': 'Misc',
Expand Down