---
toc_max_heading_level: 4
sidebar_position: 5
sidebar_label: Rust
title: TDengine Rust Connector
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

import Preparation from "./_preparation.mdx"
import RustInsert from "../../07-develop/03-insert-data/_rust_sql.mdx"
import RustBind from "../../07-develop/03-insert-data/_rust_stmt.mdx"
import RustQuery from "../../07-develop/04-query-data/_rust.mdx"

[`taos`][taos] is the official Rust language connector for TDengine. Rust developers can develop applications to access the TDengine instance data.

Rust connector provides two ways to establish connections. One is the **Native Connection**, which connects to TDengine instances via the TDengine client driver (taosc). The other is **Websocket connection**, which connects to TDengine instances via taosAdapter service.

The source code is hosted on [taosdata/taos-connector-rust](https://github.com/taosdata/taos-connector-rust).

## Supported platforms

The platforms supported by native connections are the same as those supported by the TDengine client driver.
REST connections are supported on all platforms that can run Rust.

## Version support

Please refer to [version support list](/reference/connector#version-support).

The Rust Connector is still under rapid development and is not guaranteed to be backward compatible before 1.0. We recommend using TDengine version 3.0 or higher to avoid known issues.

## Installation

### Pre-installation

* Install the Rust development toolchain
* If using the native connection, please install the TDengine client driver. Please refer to [install client driver](/reference/connector#install-client-driver)

### Add dependencies

Add the dependency to the [Rust](https://rust-lang.org) project as follows, depending on the connection method selected.

<Tabs defaultValue="default">
<TabItem value="default" label="Both">

Add [taos] to the `Cargo.toml` file.

```toml
[dependencies]
# use default feature
taos = "*"
```

</TabItem>
<TabItem value="native" label="Native only">

Add [taos] to the `Cargo.toml` file.

```toml
[dependencies]
taos = { version = "*", default-features = false, features = ["native"] }
```

</TabItem>
<TabItem value="rest" label="Websocket only">

Add [taos] to the `Cargo.toml` file and enable the `ws` feature.

```toml
[dependencies]
taos = { version = "*", default-features = false, features = ["ws"] }
```

</TabItem>
</Tabs>

## Create a connection

In rust connector, we use a DSN connection string as a connection builder. For example,

```rust
let builder = TaosBuilder::from_dsn("taos://")?;
```

You can now use connection client to create the connection.

```rust
let conn = builder.build()?;
```

The connection object can create more than one.

```rust
let conn1 = builder.build()?;
let conn2 = builder.build()?;
```

DSN is short for **D**ata **S**ource **N**ame string - [a data structure used to describe a connection to a data source](https://en.wikipedia.org/wiki/Data_source_name).

A common DSN is basically constructed as this:

```text
<driver>[+<protocol>]://[[<username>:<password>@]<host>:<port>][/<database>][?<p1>=<v1>[&<p2>=<v2>]]
|------|------------|---|-----------|-----------|------|------|------------|-----------------------|
|driver|   protocol |   | username  | password  | host | port |  database  |  params               |
```

- **Driver**: the main entrypoint to a processer. **Required**. In Rust connector, the supported driver names are listed here:
  - **taos**: the legacy TDengine connection data source.
  - **tmq**: subscription data source from TDengine.
  - **http/ws**: use websocket protocol via `ws://` scheme.
  - **https/wss**: use websocket protocol via `wss://` scheme.
- **Protocol**: the additional information appended to driver, which can be be used to support different kind of data sources. By default, leave it empty for native driver(only under feature "native"), and `ws/wss` for websocket driver (only under feature "ws"). **Optional**.
- **Username**: as its definition, is the username to the connection. **Optional**.
- **Password**: the password of the username. **Optional**.
- **Host**: address host to the datasource. **Optional**.
- **Port**: address port to the datasource. **Optional**.
- **Database**: database name or collection name in the datasource. **Optional**.
- **Params**: a key-value map for any other informations to the datasource. **Optional**.

Here is a simple DSN connection string example:

```text
taos+ws://localhost:6041/test
```

which means connect `localhost` with port `6041` via `ws` protocol, and make `test` as the default database.

So that you can use DSN to specify connection protocol at runtime:

```rust
use taos::*; // use it like a `prelude` mod, we need some traits at next.

// use native protocol.
let builder = TaosBuilder::from_dsn("taos://localhost:6030")?;
let conn1 = builder.build();

//  use websocket protocol.
let conn2 = TaosBuilder::from_dsn("taos+ws://localhost:6041")?;
```

After connected, you can perform the following operations on the database.

```rust
async fn demo(taos: &Taos, db: &str) -> Result<(), Error> {
    // prepare database
    taos.exec_many([
        format!("DROP DATABASE IF EXISTS `{db}`"),
        format!("CREATE DATABASE `{db}`"),
        format!("USE `{db}`"),
    ])
    .await?;

    let inserted = taos.exec_many([
        // create super table
        "CREATE TABLE `meters` (`ts` TIMESTAMP, `current` FLOAT, `voltage` INT, `phase` FLOAT) \
         TAGS (`groupid` INT, `location` BINARY(16))",
        // create child table
        "CREATE TABLE `d0` USING `meters` TAGS(0, 'Los Angles')",
        // insert into child table
        "INSERT INTO `d0` values(now - 10s, 10, 116, 0.32)",
        // insert with NULL values
        "INSERT INTO `d0` values(now - 8s, NULL, NULL, NULL)",
        // insert and automatically create table with tags if not exists
        "INSERT INTO `d1` USING `meters` TAGS(1, 'San Francisco') values(now - 9s, 10.1, 119, 0.33)",
        // insert many records in a single sql
        "INSERT INTO `d1` values (now-8s, 10, 120, 0.33) (now - 6s, 10, 119, 0.34) (now - 4s, 11.2, 118, 0.322)",
    ]).await?;

    assert_eq!(inserted, 6);
    let mut result = taos.query("select * from `meters`").await?;

    for field in result.fields() {
        println!("got field: {}", field.name());
    }

    let values = result.
}
```

Rust connector provides two kinds of ways to fetch data:

```rust
    // Query option 1, use rows stream.
    let mut rows = result.rows();
    while let Some(row) = rows.try_next().await? {
        for (name, value) in row {
            println!("got value of {}: {}", name, value);
        }
    }

    // Query options 2, use deserialization with serde.
    #[derive(Debug, serde::Deserialize)]
    #[allow(dead_code)]
    struct Record {
        // deserialize timestamp to chrono::DateTime<Local>
        ts: DateTime<Local>,
        // float to f32
        current: Option<f32>,
        // int to i32
        voltage: Option<i32>,
        phase: Option<f32>,
        groupid: i32,
        // binary/varchar to String
        location: String,
    }

    let records: Vec<Record> = taos
        .query("select * from `meters`")
        .await?
        .deserialize()
        .try_collect()
        .await?;

    dbg!(records);
    Ok(())
```

## Usage examples

### Write data

#### SQL Write

<RustInsert />

#### Stmt bind

<RustBind />

### Query data

<RustQuery />|

## API Reference

### Connector builder

Use DSN to directly construct a TaosBuilder object.

```rust
let builder = TaosBuilder::from_dsn("")? ;
```

Use `builder` to create many connections:

```rust
let conn: Taos = cfg.build();
```

### Connection pool

In complex applications, we recommend enabling connection pools. Connection pool for [taos] is implemented using [r2d2] by enabling "r2d2" feature.

Basically, a connection pool with default parameters can be generated as:

```rust
let pool = TaosBuilder::from_dsn(dsn)?.pool()?;
```

You can set the connection pool parameters using the `PoolBuilder`.

```rust
let dsn = "taos://localhost:6030";

let opts = PoolBuilder::new()
    .max_size(5000) // max connections
    .max_lifetime(Some(Duration::from_secs(60 * 60))) // lifetime of each connection
    .min_idle(Some(1000)) // minimal idle connections
    .connection_timeout(Duration::from_secs(2));

let pool = TaosBuilder::from_dsn(dsn)?.with_pool_builder(opts)?;
```

In the application code, use `pool.get()?` to get a connection object [Taos].

```rust
let taos = pool.get()? ;
```

### Connection methods

The [Taos] connection struct provides several APIs for convenient use.

1. `exec`: Execute some non-query SQL statements, such as `CREATE`, `ALTER`, `INSERT` etc. and return affected rows (only meaningful to `INSERT`).

    ```rust
    let affected_rows = taos.exec("INSERT INTO tb1 VALUES(now, NULL)").await?;
    ```

2. `exec_many`: You can execute many SQL statements in order with `exec_many` method.

    ```rust
    taos.exec_many([
        "CREATE DATABASE test",
        "USE test",
        "CREATE TABLE `tb1` (`ts` TIMESTAMP, `val` INT)",
    ]).await?;
    ```

3. `query`: Execute the query statement and return the [ResultSet] object.

    ```rust
    let mut q = taos.query("select * from log.logs").await?
    ```

    The [ResultSet] object stores the query result data and basic information about the returned columns (column name, type, length).

    Get filed information with `fields` method.

    ```rust
    let cols = q.fields();
    for col in cols {
        println!("name: {}, type: {:?} , bytes: {}", col.name(), col.ty(), col.bytes());
    }
    ```

    Users could fetch data by rows.

    ```rust
    let mut rows = result.rows();
    let mut nrows = 0;
    while let Some(row) = rows.try_next().await? {
        for (col, (name, value)) in row.enumerate() {
            println!(
                "[{}] got value in col {} (named `{:>8}`): {}",
                nrows, col, name, value
            );
        }
        nrows += 1;
    }
    ```

    Or use it with [serde](https://serde.rs) deserialization.

    ```rust
    #[derive(Debug, Deserialize)]
    struct Record {
        // deserialize timestamp to chrono::DateTime<Local>
        ts: DateTime<Local>,
        // float to f32
        current: Option<f32>,
        // int to i32
        voltage: Option<i32>,
        phase: Option<f32>,
        groupid: i32,
        // binary/varchar to String
        location: String,
    }

    let records: Vec<Record> = taos
        .query("select * from `meters`")
        .await?
        .deserialize()
        .try_collect()
        .await?;
    ```

Note that Rust asynchronous functions and an asynchronous runtime are required.

[Taos] provides a few Rust methods that encapsulate SQL to reduce the frequency of `format!` code blocks.

- `.describe(table: &str)`: Executes `DESCRIBE` and returns a Rust data structure.
- `.create_database(database: &str)`: Executes the `CREATE DATABASE` statement.
- `.use_database(database: &str)`: Executes the `USE` statement.

### Bind API

Similar to the C interface, Rust provides the bind interface's wrapping. First, create a bind object [Stmt] for a SQL command with the [Taos] object.

```rust
let mut stmt = Stmt::init(&taos).await?;
stmt.prepare("INSERT INTO ? USING meters TAGS(?, ?) VALUES(?, ?, ?, ?)")?;
```

The bind object provides a set of interfaces for implementing parameter binding.

#### `.set_tbname(name)`

To bind table names.

```rust
let mut stmt = taos.stmt("insert into ? values(? ,?)")?;
stmt.set_tbname("d0")?;
```

#### `.set_tags(&[tag])`

Bind tag values when the SQL statement uses a super table.

```rust
let mut stmt = taos.stmt("insert into ? using stb0 tags(?) values(? ,?)")?;
stmt.set_tbname("d0")?;
stmt.set_tags(&[Value::VarChar("涛思".to_string())])?;
```

#### `.bind(&[column])`

Bind value types. Use the [ColumnView] structure to construct the desired type and bind.

```rust
let params = vec![
    ColumnView::from_millis_timestamp(vec![164000000000]),
    ColumnView::from_bools(vec![true]),
    ColumnView::from_tiny_ints(vec![i8::MAX]),
    ColumnView::from_small_ints(vec![i16::MAX]),
    ColumnView::from_ints(vec![i32::MAX]),
    ColumnView::from_big_ints(vec![i64::MAX]),
    ColumnView::from_unsigned_tiny_ints(vec![u8::MAX]),
    ColumnView::from_unsigned_small_ints(vec![u16::MAX]),
    ColumnView::from_unsigned_ints(vec![u32::MAX]),
    ColumnView::from_unsigned_big_ints(vec![u64::MAX]),
    ColumnView::from_floats(vec![f32::MAX]),
    ColumnView::from_doubles(vec![f64::MAX]),
    ColumnView::from_varchar(vec!["ABC"]),
    ColumnView::from_nchar(vec!["涛思数据"]),
];
let rows = stmt.bind(&params)?.add_batch()?.execute()?;
```

#### `.execute()`

Execute to insert all bind records. [Stmt] objects can be reused, re-bind, and executed after execution. Remember to call `add_batch` before `execute`.

```rust
stmt.add_batch()?.execute()?;

// next bind cycle.
// stmt.set_tbname()? ;
//stmt.bind()? ;
//stmt.add_batch().execute()? ;
```

A runnable example for bind can be found [here](https://github.com/taosdata/taos-connector-rust/blob/main/examples/bind.rs).

### Subscription API

Users can subscribe a [TOPIC](../../../taos-sql/tmq/) with TMQ(the TDengine Message Queue) API.

Start from a TMQ builder:

```rust
let tmq = TmqBuilder::from_dsn("taos://localhost:6030/?group.id=test")?;
```

Build a consumer:

```rust
let mut consumer = tmq.build()?;
```

Subscribe a topic:

```rust
consumer.subscribe(["tmq_meters"]).await?;
```

Consume messages, and commit the offset for each message.

```rust
{
    let mut stream = consumer.stream();

    while let Some((offset, message)) = stream.try_next().await? {
        // get information from offset

        // the topic
        let topic = offset.topic();
        // the vgroup id, like partition id in kafka.
        let vgroup_id = offset.vgroup_id();
        println!("* in vgroup id {vgroup_id} of topic {topic}\n");

        if let Some(data) = message.into_data() {
            while let Some(block) = data.fetch_raw_block().await? {
                // one block for one table, get table name if needed
                let name = block.table_name();
                let records: Vec<Record> = block.deserialize().try_collect()?;
                println!(
                    "** table: {}, got {} records: {:#?}\n",
                    name.unwrap(),
                    records.len(),
                    records
                );
            }
        }
        consumer.commit(offset).await?;
    }
}
```

Unsubscribe:

```rust
consumer.unsubscribe().await;
```

In TMQ DSN, you must choose to subscribe with a group id. Also, there's several options could be set:

- `group.id`: **Required**, a group id is any visible string you set.
- `client.id`: a optional client description string.
- `auto.offset.reset`: choose to subscribe from *earliest* or *latest*, default is *none* which means 'earliest'.
- `enable.auto.commit`: automatically commit with specified time interval. By default - in the recommended way _ you must use `commit` to ensure that you've consumed the messages correctly, otherwise, consumers will received repeated messages when re-subscribe.
- `auto.commit.interval.ms`: the auto commit interval in milliseconds.

Check the whole subscription example at [GitHub](https://github.com/taosdata/taos-connector-rust/blob/main/examples/subscribe.rs).

Please move to the Rust documentation hosting page for other related structure API usage instructions: <https://docs.rs/taos>.

[TDengine]: https://github.com/taosdata/TDengine
[r2d2]: https://crates.io/crates/r2d2
[Taos]: https://docs.rs/taos/latest/taos/struct.Taos.html
[ResultSet]: https://docs.rs/taos/latest/taos/struct.ResultSet.html
[Value]: https://docs.rs/taos/latest/taos/enum.Value.html
[Stmt]: https://docs.rs/taos/latest/taos/stmt/struct.Stmt.html
[taos]: https://crates.io/crates/taos
