Rust – DECODE https://decode.red/blog data decode, decoder or decoded ... design of code Mon, 15 Dec 2025 06:15:00 +0000 ja hourly 1 https://wordpress.org/?v=4.7.29 Rust SQLite ../../../202501251855/ Sat, 25 Jan 2025 08:13:39 +0000 ../../../?p=1855 RustでSQLiteを扱うためのライブラリ、Rusqliteを試してみました。
Rusqliteを知ったのも、コードを書いたのも、ChatGPTとの対話からです。

use rusqlite::{params, Connection, Result};

fn main() -> Result<()> {
    // SQLiteデータベースに接続(ファイルが存在しない場合は作成される)
    let conn = Connection::open("sample.db")?;

    // テーブルを作成
    conn.execute(
        "CREATE TABLE IF NOT EXISTS users (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL,
            email TEXT NOT NULL UNIQUE
        )",
        [],
    )?;
/* 

    // データを挿入
    conn.execute(
        "INSERT INTO users (name, email) VALUES (?1, ?2)",
        params!["Alice", "alice@example.com"],
    )?;
    conn.execute(
        "INSERT INTO users (name, email) VALUES (?1, ?2)",
        params!["Bob", "bob@example.com"],
    )?;
*/

    insert_user_if_not_exists(&conn, "Alice", "alice@example.com")?;
    insert_user_if_not_exists(&conn, "Bob", "bob@example.com")?;
    insert_user_if_not_exists(&conn, "Alice", "alice@example.com")?; // 重複

    // データを取得
    /* 
    let mut stmt = conn.prepare("SELECT id, name, email FROM users")?;
    let user_iter = stmt.query_map([], |row| {
        Ok(User {
            id: row.get(0)?,
            name: row.get(1)?,
            email: row.get(2)?,
        })
    })?;
    // データを表示
    println!("Users in the database:");
    for user in user_iter {
        println!("{:?}", user?);
    }
    */
    // データを取得して構造体に格納
    let users = fetch_users(&conn)?;
    for user in users {
        //println!("{:?}", user);
        // 各フィールドを展開して表示
        println!(
            "ID: {}, Name: {}, Email: {}",
            user.id, user.name, user.email
        );
    }
    Ok(())
}

// データを保持するための構造体
#[derive(Debug)]
struct User {
    id: i32,
    name: String,
    email: String,
}

fn insert_user_if_not_exists(conn: &Connection, name: &str, email: &str) -> Result<()> {
    if !is_duplicate(conn, "users", "email", email)? {
        insert_user(conn, name, email)?;
        println!("Inserted: {}, {}", name, email);
    } else {
        println!("Email '{}' already exists. Skipping insert.", email);
    }
    Ok(())
}
fn is_duplicate(conn: &Connection, table: &str, column: &str, value: &str) -> Result<bool> {
    let query = format!(
        "SELECT EXISTS(SELECT 1 FROM {} WHERE {} = ?1)",
        table, column
    );
    let exists: bool = conn.query_row(&query, params![value], |row| row.get(0))?;
    Ok(exists)
}
fn insert_user(conn: &Connection, name: &str, email: &str) -> Result<()> {
    conn.execute(
        "INSERT INTO users (name, email) VALUES (?1, ?2)",
        params![name, email],
    )?;
    Ok(())
}

/// データベースから`User`構造体のリストを取得する関数
fn fetch_users(conn: &Connection) -> Result<Vec<User>> {
    let mut stmt = conn.prepare("SELECT id, name, email FROM users")?;
    let user_iter = stmt.query_map([], |row| {
        Ok(User {
            id: row.get(0)?,
            name: row.get(1)?,
            email: row.get(2)?,
        })
    })?;

    // 結果をベクタに変換
    let users: Vec<User> = user_iter.filter_map(Result::ok).collect();
    Ok(users)
}

コメントの部分は、修正前のコードを残しています。
存在のチェックをしたり、関数化をしたり、ChatGPTにリクエストしています。
構造体のメンバーを使っていないことからワーニングが出ていたため、構造体に格納するよに指示しました。

Rustは構文チェックが厳しくエラーが出やすいのですが、ChatGPTの活用でストレスが少なくなります。

]]>
Call Rust from Golang ../../../202309031697/ Sun, 03 Sep 2023 06:45:00 +0000 ../../../?p=1697 今回はGo、Rustの相互呼び出しを試してみました。これまでCを介していましたが直接呼出しです。

参考)https://qiita.com/momotaro98/items/92e39e214b0e92f454a7

環境)

$ gcc -v

gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.1)
$ go version
go version go1.20.4 linux/amd64
$ cargo version
cargo 1.69.0 (6e9a83356 2023-04-12)
$ rustc –version
rustc 1.69.0 (84c898d65 2023-04-16)

Call Rust

callrs.go

package main
/*
#cgo LDFLAGS: -L./lib -lhello
#include <stdlib.h>
#include "./lib/hello.h"
*/
import "C"
import (
    "fmt"
    "unsafe"
)
func main() {
    s := "Go String"

    input := C.CString(s)
    defer C.free(unsafe.Pointer(input))

    o := C.hello(input)

    output := C.GoString(o)
    fmt.Printf("Go main: \"%s\"\n", output)
}

lib/hello/src/lib.rs

extern crate libc;
use std::ffi::{CStr, CString};

#[no_mangle]
pub extern "C" fn hello(name: *const libc::c_char) -> *const libc::c_char {
    let cstr = unsafe { CStr::from_ptr(name) };
    let s = cstr.to_str().unwrap().to_string();
    println!("Rust func hello: \"{}\"", s);
    let rstr: &str = "Rust String";
    CString::new(rstr).unwrap().into_raw()
}

lib/hello.h

char* hello(char *name);

lib/hello/Cargo.toml

[package]
name = "hello"
version = "0.1.0"

[lib]
crate-type = ["cdylib"]

[dependencies]
libc = "0.2.2"

Makefile

ROOT_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST))))

build:
        cd lib/hello && cargo build --release
        cp lib/hello/target/release/libhello.so lib/
        echo 'ROOT_DIR is $(ROOT_DIR)'
        go build -ldflags="-r $(ROOT_DIR)lib" callrs.go

実行画面

$ ./callrs
Rust func hello: “Go String”
Go main: “Rust String”

Call Go

src/main.rs

use std::ffi::{CStr, CString};
use std::os::raw::c_char;

extern "C" {
    fn hello(name: GoString) -> *const c_char;
}

#[repr(C)]
struct GoString {
    a: *const c_char,
    b: i64,
}

fn main() {
    let s = CString::new("Rust String").expect("CString::new failed");
    let ptr = s.as_ptr();
    let input = GoString {
        a: ptr,
        b: s.as_bytes().len() as i64,
    };

    let result = unsafe { hello(input) };
    let c_str = unsafe { CStr::from_ptr(result) };
    let output = c_str.to_str().expect("to_str failed");
    println!("Rust main: \"{}\"", output);
}

golib/main.go

package main

import "C"
import "fmt"

//export hello
func hello(s string) *C.char {
        fmt.Printf("Go Func hello: \"%s\" \n", s)
        str := "Go String"
        return C.CString(str)
}

func main() {}

golib/libhello.h (自動生成)

build.rs

fn main() {
    let path = "./golib";
    let lib = "hello";

    println!("cargo:rustc-link-search=native={}", path);
    println!("cargo:rustc-link-lib=static={}", lib);
}

Cargo.toml

[package]
name = "callgo"
version = "0.1.0"

Makefile

build:
        cd golib && go build -buildmode=c-archive -o libhello.a main.go
        cargo build
        cp target/debug/callgo .

実行画面

$ ./callgo
Go Func hello: “Rust String”
Rust main: “Go String”

参考サイトに丁寧にかかれていますので、とても参考になりました。
実行場所と文字列の受け渡しをわかりやすくしてみました。
やはりRustは記述が複雑です。それゆえ事例を残しておくことで、あとで参照したいと思っています。

]]>
Call C code from Rust ../../../202308131691/ Sun, 13 Aug 2023 00:04:34 +0000 ../../../?p=1691 Golangにつづき、重要言語であるRustについてもCとのインターフェイスを調べてみました。

「Call C code from Golang」

Call C code from Golang

環境)
gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.1)
rustc 1.69.0 (84c898d65 2023-04-16)

hello.c

#include<stdio.h>

void hello(){
    printf("hello!\n");
}

callc.rs

#[link(name="hello", kind="static")]
extern{
    fn hello();
}

fn main() {
    unsafe {hello();};
}

$ gcc -o hello.o -c hello.c
$ ar rcs libhello.a hello.o
$ rustc -L. callc.rs
$ ./callc
hello!

hello.rs

#![crate_type = "cdylib"]

#[no_mangle]
pub extern "C" fn hello(){
   println!("hello!");
}

callrs.c

#include <stdio.h>

void hello(void);

int main(int argc, char **argv){
    hello();
    return 0;
}

$ rustc hello.rs
$ gcc -o callrs.o -c callrs.c
$ gcc -o callrs callrs.o -L. -lhello
$ ldd callrs
linux-vdso.so.1 (0x00007ffcecfcb000)
libhello.so => not found
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f96a0efc000)
/lib64/ld-linux-x86-64.so.2 (0x00007f96a110a000)
$ LD_LIBRARY_PATH=. ./callrs
hello!

引数についてもテストしたかったのですが、ここまででもバージョンによるオプション違いとか、いろいろと迷うところがあったため、まずは自環境での動作を確認する意味でここまでとしました。
rustよびだしは、shared objectなります。(libhello.so) また最小限のやり方を試す意味で rustcを直接使用しました。

参考)https://mmi.hatenablog.com/entry/2017/02/28/213656

]]>
WebAssembly Runtime ../../../202307231666/ Sun, 23 Jul 2023 06:48:46 +0000 ../../../?p=1666 wasm(WebAssembly)のランタイムについて、いろいろ調べてみました。
WebAssemblyは何度も扱ってきていますが、WATコード(WebAssembly Text)からコンパイルを試したことがありませんでした。まずは基本を押さえたいと思います。(参考:翔泳社「入門WebAssembly」)

環境) M2 Mac

myadd.wat

(module
	(func (export "myadd")
		(param $value.1 i32) (param $value.2 i32)
		(result i32)
		local.get $value.1
		local.get $value.2
		i32.add
	)
)

value 1,2をスタックに積んで、関数呼び出し。まさにアセンブラですね。

myadd.js

const fs = require('fs');

const bytes = fs.readFileSync(__dirname + '/myadd.wasm');
const v1 = parseInt(process.argv[2]);
const v2 = parseInt(process.argv[3]);

(async () => {
	const obj = await WebAssembly.instantiate(new Uint8Array(bytes));
	let ret = obj.instance.exports.myadd(v1, v2);
	console.log("%d + %d = %d", v1, v2, ret);
})();

% node -v
v18.12.1
% npm -v
8.19.2
% npm install -g wat-wasm

% wat2wasm myadd.wat

  Contact Rick Battagline
  Twitter: @battagline
  https://wasmbook.com
  v1.0.43

no memory
Writing to myadd.wasm
WASM File Saved!

% node myadd.js 1 2
1 + 2 = 3

wat2wasmで、バイトコードに、そしてそれをnode.jsから呼び出しました。

次は、node.jsではなく、wasmtimeというランタイムを使って実行してみます。(brew でインストール)

引用元)https://github.com/bytecodealliance/wasmtime/blob/main/docs/WASI-tutorial.md

demo.wat

(module
    ;; Import the required fd_write WASI function which will write the given io vectors to stdout
    ;; The function signature for fd_write is:
    ;; (File Descriptor, *iovs, iovs_len, nwritten) -> Returns number of bytes written
    (import "wasi_unstable" "fd_write" (func $fd_write (param i32 i32 i32 i32) (result i32)))

    (memory 1)
    (export "memory" (memory 0))

    ;; Write 'hello world\n' to memory at an offset of 8 bytes
    ;; Note the trailing newline which is required for the text to appear
    (data (i32.const 8) "hello world\n")

    (func $main (export "_start")
        ;; Creating a new io vector within linear memory
        (i32.store (i32.const 0) (i32.const 8))  ;; iov.iov_base - This is a pointer to the start of the 'hello world\n' string
        (i32.store (i32.const 4) (i32.const 12))  ;; iov.iov_len - The length of the 'hello world\n' string

        (call $fd_write
            (i32.const 1) ;; file_descriptor - 1 for stdout
            (i32.const 0) ;; *iovs - The pointer to the iov array, which is stored at memory location 0
            (i32.const 1) ;; iovs_len - We're printing 1 string stored in an iov - so one.
            (i32.const 20) ;; nwritten - A place in memory to store the number of bytes written
        )
        drop ;; Discard the number of bytes written from the top of the stack
    )
)

% wasmtime -V
wasmtime-cli 10.0.1
% wasmtime demo.wat
hello world
% wat2wasm demo.wat

  Contact Rick Battagline
  Twitter: @battagline
  https://wasmbook.com
  v1.0.43

memory found
Writing to demo.wasm
WASM File Saved!
% wasmtime demo.wasm
hello world

先ほどのWATコードでは、実行はできますが標準出力に書かないためコードを変えています。
(watから直接実行もできるもよう)

ファイル確認

% file demo.wasm
demo.wasm: WebAssembly (wasm) binary module version 0x1 (MVP)
% file myadd.wasm
myadd.wasm: WebAssembly (wasm) binary module version 0x1 (MVP)

次は、Rustのコードからコンパイルするため、環境をdockerで構築しました。
引用元) https://qiita.com/toshikisugiyama/items/168cdbf834156b1f1e70

docker-compose.yml

services:
  rust:
    image: rust:latest
    volumes:
      - .:/projects
    working_dir: /projects
    environment:
      - USER=user

% docker compose run –rm rust

このコマンドで一気に環境を作り、コンテナのシェルの状態になります。

root@f4b90a7fb5a9:/projects# cargo new hello_cargo
  Created binary (application) hello_cargo package

root@f4b90a7fb5a9:/projects# ls
docker-compose.yml hello_cargo

root@f4b90a7fb5a9:/projects# cd hello_cargo/
root@f4b90a7fb5a9:/projects/hello_cargo# ls
Cargo.toml src

root@f4b90a7fb5a9:/projects/hello_cargo# cargo run
  Compiling hello_cargo v0.1.0 (/projects/hello_cargo)
  Finished dev [unoptimized + debuginfo] target(s) in 1.07s
  Running target/debug/hello_cargo
Hello, world!

デフオルトプロジェクトの状態で実行できることを確認できました。(これはwasmではない)

hello_cargo/src/main.rs

fn main() {
    println!("Hello, world!");
}

カレントフォルダとコンテナの/projectsが、マウントされているため、ファイルの編集は別コンソールで、できます。(コマンド実行だけコンテナからやればよいことにことに)

そして、この環境を使って、wasmtimeの例を動かしてみます。
引用元) https://wasmtime.dev/

root@f4b90a7fb5a9:/projects# rustup target add wasm32-wasi
info: downloading component ‘rust-std’ for ‘wasm32-wasi’
info: installing component ‘rust-std’ for ‘wasm32-wasi’
root@f4b90a7fb5a9:/projects# rustc hello.rs –target wasm32-wasi
root@f4b90a7fb5a9:/projects# wasmtime hello.wasm
bash: wasmtime: command not found

コンテナにはwasmtimeを入れていないので、カレントに戻り、

% wasmtime hello.wasm
Hello, world!

(hello.rsは、上記 hello_cargo/src/main.rs と全く同じ)
Rustコードをwasmにコンパイルして実行できることを確認しました。

そして次は、wasmをコンテナにして実行してみます。
引用元) https://developer.mamezou-tech.com/blogs/2023/01/25/using-wasm-on-docker/

% docker run -dp 8080:8080 –name=wasm-example –runtime=io.containerd.wasmedge.v1 –platform=wasi/wasm32 michaelirwin244/wasm-example
Unable to find image ‘michaelirwin244/wasm-example:latest’ locally
latest: Pulling from michaelirwin244/wasm-example
docker: operating system is not supported.
See ‘docker run –help’.

Use containerd for pulling and storing images -> on にします。

% docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
029a5376af41 michaelirwin244/wasm-example “/hello_world.wasm” 14 seconds ago Up 9 seconds 0.0.0.0:8080->8080/tcp wasm-example
% docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
michaelirwin244/wasm-example latest 1b0714a5af55 37 seconds ago 4.14MB

% curl localhost:8080
Hello world from Rust running with Wasm! Send POST data to /echo to have it echoed back to you%
% curl -X POST -d abcdefg localhost:8080
% curl -X POST -d abcdefg localhost:8080/echo
abcdefg%

うまく立ち上がり動作も確認できました。(POST /echo の場合はBodyをEcho)

でもこれではwasmかどうかわかりませんので、サーバの部分を単体で動かしてみます。
(引用元の情報を参考にダウンロード)

% git clone https://github.com/mikesir87/wasm-example.git

% cd wasm-example

コンテナでコマンド実行

root@bea36f5878ef:/projects/wasm-example# rustup target add wasm32-wasi
info: downloading component ‘rust-std’ for ‘wasm32-wasi’
info: installing component ‘rust-std’ for ‘wasm32-wasi’
root@bea36f5878ef:/projects/wasm-example# cargo build –target wasm32-wasi –release

カレントに戻りwasmtime実行

% wasmtime run –tcplisten 127.0.0.1:8080 target/wasm32-wasi/release/hello_world.wasm
Error: failed to run main module target/wasm32-wasi/release/hello_world.wasm

Caused by:
  0: failed to instantiate “target/wasm32-wasi/release/hello_world.wasm”
  1: unknown import: wasi_snapshot_preview1::sock_setsockopt has not been defined

うまく行かず。。。
Runtimeをwasmedgeに差し替えてチャレンジ。

% curl -sSf https://raw.githubusercontent.com/WasmEdge/WasmEdge/master/utils/install.sh | bash
Using Python: /Users/z/.pyenv/versions/miniforge3-4.10.3-10/bin/python3
INFO – Compatible with current configuration
INFO – Running Uninstaller
WARNING – Uninstaller did not find previous installation
WARNING – SHELL variable not found. Using zsh as SHELL
INFO – shell configuration updated
INFO – Downloading WasmEdge
|============================================================|100.00 %INFO – Downloaded
INFO – Installing WasmEdge
INFO – WasmEdge Successfully installed
INFO – Run:
source /Users/z/.zshenv

% wasmedge target/wasm32-wasi/release/hello_world.wasm
Server is now running

これで、さほどのPOST /echo のcurlアクセスもうまくいきました。(dockerのwasm runtimeはwasmtimeと違うようです)

wasm-example/src/main.rs

use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Method, Request, Response, StatusCode, Server};
use std::convert::Infallible;
use std::net::SocketAddr;
use std::result::Result;

async fn handle_request(req: Request<Body>) -> Result<Response<Body>, anyhow::Error> {
    match (req.method(), req.uri().path()) {
        (&Method::GET, "/") => Ok(Response::new(Body::from(
            "Hello world from Rust running with Wasm! Send POST data to /echo to have it echoed back to you",
        ))),

        // Simply echo the body back to the client.
        (&Method::POST, "/echo") => Ok(Response::new(req.into_body())),

        // Return the 404 Not Found for other routes.
        _ => {
            let mut not_found = Response::default();
            *not_found.status_mut() = StatusCode::NOT_FOUND;
            Ok(not_found)
        }
    }
}

#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let addr = SocketAddr::from(([0, 0, 0, 0], 8080));
    let make_svc = make_service_fn(|_| {
        async move {
            Ok::<_, Infallible>(service_fn(move |req| {
                handle_request(req)
            }))
        }
    });
    let server = Server::bind(&addr).serve(make_svc);
    println!("Server is now running");
    if let Err(e) = server.await {
        eprintln!("server error: {}", e);
    }
    Ok(())
}

ここまでwasmの動かし方について、いろいろ試してみました。前回Goを試しましたがいろいろ調べてみるとRustが非常に向いていると感じました。(まだ関数コールをやる必要がありますが、これは次の機会に・・)
下記サイトに、Goとwasmの関係について、とても参考になる記事があります。

「GoのWASMがライブラリではなくアプリケーションであること」
https://www.kabuku.co.jp/developers/annoying-go-wasm

RustはOSすらないベアメタル環境でもプログラムを動作させることができます。様々なところで実行できるwasmだけに、こういった成り立ちの違いが大きく影響するのではと感じました。

]]>
Web Assembly (2) ../../../202204021431/ Sat, 02 Apr 2022 07:42:45 +0000 ../../../?p=1431 前回とは違って、コンソールでビルドしてみました。WebAssembly System Interface のTurtorial を使います。

https://github.com/bytecodealliance/wasmtime/blob/main/docs/WASI-tutorial.md

とても重要なコードなので、CとRustそれぞれ引用させていただきました。

demo.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

int main(int argc, char **argv) {
    ssize_t n, m;
    char buf[BUFSIZ];

    if (argc != 3) {
        fprintf(stderr, "usage: %s <from> <to>\n", argv[0]);
        exit(1);
    }

    int in = open(argv[1], O_RDONLY);
    if (in < 0) {
        fprintf(stderr, "error opening input %s: %s\n", argv[1], strerror(errno));
        exit(1);
    }

    int out = open(argv[2], O_WRONLY | O_CREAT, 0660);
    if (out < 0) {
        fprintf(stderr, "error opening output %s: %s\n", argv[2], strerror(errno));
        exit(1);
    }

    while ((n = read(in, buf, BUFSIZ)) > 0) {
        char *ptr = buf;
        while (n > 0) {
            m = write(out, ptr, (size_t)n);
            if (m < 0) {
                fprintf(stderr, "write error: %s\n", strerror(errno));
                exit(1);
            }
            n -= m;
            ptr += m;
        }
    }

    if (n < 0) {
        fprintf(stderr, "read error: %s\n", strerror(errno));
        exit(1);
    }

    return EXIT_SUCCESS;
}

main.rs

use std::env;
use std::fs;
use std::io::{Read, Write};

fn process(input_fname: &str, output_fname: &str) -> Result<(), String> {
    let mut input_file =
        fs::File::open(input_fname).map_err(|err| format!("error opening input {}: {}", input_fname, err))?;
    let mut contents = Vec::new();
    input_file
        .read_to_end(&mut contents)
        .map_err(|err| format!("read error: {}", err))?;

    let mut output_file = fs::File::create(output_fname)
        .map_err(|err| format!("error opening output {}: {}", output_fname, err))?;
    output_file
        .write_all(&contents)
        .map_err(|err| format!("write error: {}", err))
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();

    if args.len() < 3 {
        eprintln!("usage: {} <from> <to>", program);
        return;
    }

    if let Err(err) = process(&args[1], &args[2]) {
        eprintln!("{}", err)
    }
}

Cのビルド

https://github.com/WebAssembly/wasi-sdk/releases/tag/wasi-sdk-14

よりSDKをダウンロードしてビルド

wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-14/wasi-sdk-14.0-linux.tar.gz
tar zxvf wasi-sdk-14.0-linux.tar.gz
./wasi-sdk-14.0/bin/clang –sysroot=./wasi-sdk-14.0/share/wasi-sysroot demo.c -o demo.wasm
file demo.wasm
demo.wasm: WebAssembly (wasm) binary module version 0x1 (MVP)

cppの場合は、clang++ を使用

Rustのビルド

rustup target add wasm32-wasi
cargo build –target wasm32-wasi

target/wasm32-wasi/debug にdemo.wasmが出力される

wasmtimeインストール(sourceコマンドでパス有効化)

curl http://wasmtime.dev/install.sh -sSf | bash

実行結果

$ echo hello > hello.txt
$ wasmtime demo.wasm
usage: demo.wasm [from] [to]
$ wasmtime –dir=. –dir=. demo.wasm hello.txt out.txt
$ cat out.txt
hello

wasmtimeはWASIのランタイムで、wasmファイルを直接実行できます。一見普通のコードとWasmは区別がつかないくらいです。
Cの場合、環境がきちんと通っていないと間違えて通常のバイナリを作ってしまいます。
wasmは一つのバイナリをいろいろなところで使うことができます。下記動画が参考になります。
(しかし古いせいか作ったモジュールが不正となってしまい動作確認がとれませんでした)

“WebAssembly Interface Types: Interoperate with All The Things!”

]]>
Rust Programming Language ../../../202105041282/ Tue, 04 May 2021 08:09:27 +0000 ../../../?p=1282 前回のDartの続きで、Rustというもう一つ注目している言語をとりあげます。(いまさら感ありますがなかなか機会がなかったので・・) C言語のようなローレベル言語が持つパフォーマンスの良さと、高い安全性、モダン言語の機能を合わせ持つというとても魅力的なものです。

参考)
スクリプトマガジン vol.64
【翻訳】RustとCSV解析 https://qiita.com/algebroid/items/c456d4ec555ae04c7f92

上記特集の最初のコードにあるreqwestクレートのWebアクセスサンプルにいきなり驚かされました。クレートというのはライブラリパッケージのようなものでこういった環境がコマンド一つですぐに整うところが好みです。

インストール(Mac)

xcode-select –install

https://rustup.rs/ にアクセスしてコマンドを取得

curl –proto ‘=https’ –tlsv1.2 -sSf https://sh.rustup.rs | sh

rustコマンドで確認

rustc -V
rustc 1.51.0 (2fd73fabe 2021-03-23)
rustup update

あとはrustコマンドを使わずビルド、実行ができます。

cargo new mypackagename
cargo build
cargo run

ソースコードは src/main.rs にデフォルトでHello Wolrdを表示するプログラムがあり、これを編集します。

csvフォーマットのデータファイルを編集することを想定して、まず読み込んで表示するプログラムを作成しました。

fn main() {
    let mut rdr = csv::Reader::from_reader(std::io::stdin());
    for r in rdr.records() {
        println!("{:?}", r);
    }
}

Cargo.toml ファイルの[dependencies]に
csv = “1.1”
と記述するだけで自動的に関連ファイルをダウンロードしてビルドしてくれます。
csvクレートは

https://crates.io/crates/csv

で、使い方を見ることができます。

実行

cargo run < test.csv

csvファイル

abc,def,ghi
111,222,333
111,222,333,444

結果

Ok(StringRecord([“111”, “222”, “333”]))
Err(Error(UnequalLengths { pos: Some(Position { byte: 24, line: 3, record: 2 }), expected_len: 3, len: 4 }))

1行目はヘッダでスキップ、2行目はデータ表示、3行目は列数が多いためランタイムエラーがでました。
動的言語ならありそうな出力(こんなに親切に出してくれないかも)ですが、C言語なら配列が十分に定義されていないのなら危ない処理です。

use std::error::Error;
  
fn main() {
    if let Err(err) = csv_read() {
        println!("{}", err);
    }
}

fn csv_read() -> Result<(), Box<dyn Error>> {
    let mut rdr = csv::Reader::from_reader(std::io::stdin());
    for r in rdr.records() {
        let rec = r?;
        println!("{:?}", rec);
    }
    Ok(())
}

StringRecord([“111”, “222”, “333”])
CSV error: record 2 (line: 3, byte: 24): found record with 4 fields, but the previous record has 3 fields

次は、1行目はヘッダでスキップ、2行目はデータ表示、3行目は列数オーバーでさっきよりさらにわかりやすいエラー表示です。
let rec = r?;

Ok(())
というのが特徴的です。
前者は、前回も引用したSwiftのOptionalと同じですね。Cの書き方でNULLポインタを回避する仕組みです。
後者の書き方は馴染みがないのですが(セミコロンなし)、Boxingという仕組みを表すための書き方です。

Rustは特集記事にもCとHaskellを融合させたものとありますが、最近の言語の機能をすべて持っていて、さらに所有権モデルという新しい機能をもあり、押さえておかなくてはいけないと強く思っています。

Haskellも好きな言語で過去に何度か記事をかきました。

http://crossframe.iiv.jp/category/haskell/
../../../tag/haskell/
http://bitlife.me/archives/tag/haskell

個人的には、コードの品質をコード規約や単体テストで改善しようとすることにくらべたら、難解な言語仕様や文法でもビルド時にデバッグできるのならの方がいいです。(生産性は慣れでカバー) という考えでHaskellも学んだのですが、このRustは十分にわかりやすい言語仕様で安全性もあるということで、後発言語ゆえの優位性を感じます。

]]>