RDS

テーブルとカラムを表現するには、hashmap< Vector > となるだろうか?

class Table:
    def __init__(self, name, columns):
        self.name = name
        self.columns = columns
        self.rows = []

    def insert(self, values):
        if len(values) != len(self.columns):
            raise ValueError("列の数と値の数が一致しません。")
        row = dict(zip(self.columns, values))
        self.rows.append(row)

    def select_all(self):
        return self.rows

    def __str__(self):
        lines = [" | ".join(self.columns)]
        lines.append("-" * len(lines[0]))
        for row in self.rows:
            lines.append(" | ".join(str(row[col]) for col in self.columns))
        return "\n".join(lines)

class Database:
    def __init__(self):
        self.tables = {}

    def create_table(self, name, columns):
        if name in self.tables:
            raise ValueError(f"テーブル '{name}' は既に存在します。")
        self.tables[name] = Table(name, columns)

    def insert_into(self, table_name, values):
        self.tables[table_name].insert(values)

    def select_all(self, table_name):
        return self.tables[table_name].select_all()

    def show_table(self, table_name):
        print(self.tables[table_name])

if __name__ == "__main__":
    db = Database()
    db.create_table("users", ["id", "name", "email"])
    db.insert_into("users", [1, "Alice", "alice@example.com"])
    db.insert_into("users", [2, "Bob", "bob@example.com"])

    print("📄 users テーブル内容:")
    db.show_table("users")

    print("\n📋 SELECT * FROM users:")
    for row in db.select_all("users"):
        print(row)

$ python3 app.py
📄 users テーブル内容:
id | name | email
—————–
1 | Alice | alice@example.com
2 | Bob | bob@example.com

📋 SELECT * FROM users:
{‘id’: 1, ‘name’: ‘Alice’, ’email’: ‘alice@example.com’}
{‘id’: 2, ‘name’: ‘Bob’, ’email’: ‘bob@example.com’}

【Python】NoSQL【Rust】

import json
import os
from typing import List, Dict, Any

class SimpleNoSQL:
    def __init__(self, db_path: str= "db.json"):
        self.db_path = db_path
        if os.path.exists(self.db_path):
            with open(self.db_path, "r") as f:
                self.data = json.load(f)
        else:
            self.data = []

    def save(self):
        with open(self.db_path, "w") as f:
            json.dump(self.data, f, indent=2)

    def insert(self, document: Dict[str, Any]):
        self.data.append(document)
        self.save()

    def find(self, query: Dict[str, Any]) -> List[Dict[str, Any]]:
        def match(doc):
            return all(doc.get(k) == v for k, v in query.items())
        return [doc for doc in self.data if match(doc)]

    def update(self, query: Dict [str, Any], update_fields: Dict[str, Any]):
        for doc in self.data:
            if all(doc.get(k) == v for k, v in query.items()):
                 doc.update(update_fields)
        self.save()

    def delete(self, query: Dict[str, Any]):
        self.data = [doc for doc in self.data if not all(doc.get(k) == v for k, v in query.items())]
        self.save

db = SimpleNoSQL()

db.insert({
    "session_id": "abc123",
    "code": "fn main() { println!(\"Hello\"); }",
    "output": "Hello",
    "status": "success"
})

results = db.find({"session_id": "abc123"})
print("検索結果:", results)

db.update({"session_id": "abc123"}, {"status": "reviewed"})

[
{
“session_id”: “abc123”,
“code”: “fn main() { println!(\”Hello\”); }”,
“output”: “Hello”,
“status”: “reviewed”
}
]

これをRustでやりたい

### Rustでのjsonの保存

use std::collections::HashMap;
use std::fs::File;
use std::io::Write;
use serde::{Serialize, Deserialize};

fn json_w() -> std::io::Result<()> {
    let mut map = HashMap::new();
    map.insert("name", "Alice");
    map.insert("city", "Tokyo");
    map.insert("language", "Rust");

    let json = serde_json::to_string_pretty(&map).expect("Failed to serialize");
    let mut file = File::create("output.json")?;
    file.write_all(json.as_bytes())?;
    
    println!("{:?}", json);
    Ok(())
}

fn main() {
    let _ = json_w();
}

### jsonの読み取り

fn json_r() {
    let data = fs::read_to_string("output.json").expect("Failed to read file");
    let map: HashMap<String, String> = serde_json::from_str(&data).expect("Failed to deserialize");

    println!("{:#?}", map);    
}

{
“name”: “Alice”,
“language”: “Rust”,
“city”: “Tokyo”,
}

### 改修
レコードごとに保存する必要があるので HashMap から Vec> に変更

fn json_w() -> std::io::Result<()> {
    let mut data: Vec<HashMap<String, String>> = match File::open("output.json") {
        Ok(mut file) => {
            let mut contents = String::new();
            file.read_to_string(&mut contents)?;
            serde_json::from_str(&contents).unwrap_or_else(|_| Vec::new())
        },
        Err(_) => Vec::new(),
    };

    let mut map = HashMap::new();
    // map.insert("name".to_string(), "Alice".to_string());
    // map.insert("city".to_string(), "Tokyo".to_string());
    // map.insert("language".to_string(), "Rust".to_string());

    map.insert("name".to_string(), "Bob".to_string());
    map.insert("city".to_string(), "Osaka".to_string());
    map.insert("language".to_string(), "Go".to_string());

    data.push(map);

    let json = serde_json::to_string_pretty(&data).expect("Failed to serialize");
    let mut file = File::create("output.json")?;
    file.write_all(json.as_bytes())?;

    println!("{:?}", json);
    Ok(())
}

fn json_r() {
    let data = fs::read_to_string("output.json").expect("Failed to read file");
    let v: Vec<HashMap<String, String>> = serde_json::from_str(&data).expect("Failed to deserialize");

    println!("{:#?}", v);    
}

fn main() {
    // 
    // let _ = json_w();
    let _ = json_r();
}

[
{
“language”: “Rust”,
“name”: “Alice”,
“city”: “Tokyo”,
},
{
“city”: “Osaka”,
“language”: “Go”,
“name”: “Bob”,
},
]

### implで表現する
関数をinsert, selectとする

use std::collections::HashMap;
use std::fs;
use std::fs::{File};
use std::io::{Read, Write};
use serde::{Serialize, Deserialize};

pub static FILE_NAME : &'static str = "output.json";

struct NoSQL {
    data: Vec<HashMap<String, String>>,
}

impl NoSQL {
    fn insert(&mut self, map: HashMap<String, String>) -> std::io::Result<()> {
        self.data = match File::open(FILE_NAME) {
            Ok(mut file) => {
                let mut contents = String::new();
                file.read_to_string(&mut contents)?;
                serde_json::from_str(&contents).unwrap_or_else(|_| Vec::new())
            },
            Err(_) => Vec::new(),
        };        
        self.data.push(map);

        let json = serde_json::to_string_pretty(&self.data).expect("Failed to serialize");
        let mut file = File::create(FILE_NAME)?;
        file.write_all(json.as_bytes())?;

        println!("{:?}", json);
        Ok(())
    }

    fn select(&self) {
        let data = fs::read_to_string(FILE_NAME).expect("Failed to read file");
        let v: Vec<HashMap<String, String>> = serde_json::from_str(&data).expect("Failed to deserialize");
    
        println!("{:#?}", v);    
    }
}

fn main() {
    let mut sql = NoSQL{ data: Vec::new() };

    let mut map = HashMap::new();
    map.insert("name".to_string(), "Bob".to_string());
    map.insert("city".to_string(), "Osaka".to_string());
    map.insert("language".to_string(), "Go".to_string());

    sql.insert(map);
    sql.select();
}

こうすると、nosqlは単純にVec>,と言える。
インメモリデータベースの場合は、メモリに保存するので、jsonではなく、単純にデータとして持っておくだけになる。その場合、サイズが動的なので、スタックではなく、ヒープに保存される。

Rustのplaygroundを作る

html

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Rust Execute</title>
  <style>
    #editor { width: 800px; height: 300px; }
    #output { white-space: pre-wrap; border: 1px solid #ccc; padding: 10px; margin-top: 10px; }
  </style>
</head>
<body>
  <h2>Rust Editor</h2>
  <div id="editor"></div>
  <button onclick="runRust()">Run</button>
  <h3>Output</h3>
  <div id="output"></div>
  <script src="https://unpkg.com/monaco-editor@0.45.0/min/vs/loader.js"></script>
  <script>
    let editor;
    require.config({ paths: { vs: 'https://unpkg.com/monaco-editor@0.45.0/min/vs' }});
    require(['vs/editor/editor.main'], function () {
      editor = monaco.editor.create(document.getElementById('editor'), {
        value: `fn main() {\n    println!("Hello from Rust!");\n}`,
        language: 'rust',
        theme: 'vs-dark'
      });
    });

    async function runRust() {
      const code = editor.getValue();
      const res = await fetch('/run-rust', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ code })
      });
      const result = await res.text();
      document.getElementById('output').innerText = result;
    }
  </script>
</body>
</html>

node

const express = require('express');
const bodyParser = require('body-parser');
const fs = require('fs');
const { exec } = require('child_process');
const path = require('path');

const app = express();
app.use(bodyParser.json());
app.use(express.static(__dirname)); 

app.post('/run-rust', (req, res) => {
    const code = req.body.code;
    const filename = `temp_${Date.now()}.rs`;
    const filepath = path.join(__dirname, filename);

    fs.writeFileSync(filepath, code);    
    const outputBinary = filepath.replace('.rs', '');

    exec(`rustc ${filepath} -o ${outputBinary}`, (err, _, stderr) => {
        if (err) {
            fs.unlinkSync(filepath);
            return res.send(`コンパイルエラー:\n${stderr}`);
        }

        exec(outputBinary, (runErr, stdout, runStderr) => {
            fs.unlinkSync(filepath);
            fs.unlinkSync(outputBinary);
      
            if (runErr) {
              return res.send(`実行エラー:\n${runStderr}`);
            }
      
            res.send(stdout);
        });
    });
});

const PORT = 3000;
const HOST = '192.168.33.10';
app.listen(PORT, HOST, () => console.log(`http://192.168.33.10:${PORT} で実行中`));

Monaco Editorを試す【Javascript】

ブラウザをエディターとして使用できるようになります。
language: ‘javascript’と書いているところで、好みの言語に変更できる。

<!Doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Monaco Editor Sample</title>
    <style>
    #container {
      width: 800px;
      height: 600px;
      border: 1px solid grey;
    }
  </style>
</head>
<body>
    <h1>Monaco Editor demo</h1>
    <div id="container"></div>
    <script src="https://unpkg.com/monaco-editor@0.45.0/min/vs/loader.js"></script>
    <script>
        require.config({ paths: { 'vs': 'https://unpkg.com/monaco-editor@0.45.0/min/vs' }});
        require(['vs/editor/editor.main'], function(){
            monaco.editor.create(document.getElementById('container'), {
                value: [
                'function hello() {',
                '\tconsole.log("Hello, Monaco!");',
                '}',
                'hello();'
                ].join('\n'),
                language: 'javascript',
                theme: 'vs-dark'
            });
        });
    </script>
</body>
</html>

なるほど!

入力内容をファイルにするには

        let editor;

        require.config({ paths: { 'vs': 'https://unpkg.com/monaco-editor@0.45.0/min/vs' }});
        require(['vs/editor/editor.main'], function(){
            editor = monaco.editor.create(document.getElementById('container'), {
                value: [
                    'fn main() {',
                    '    println!("Hello, Rust!");',
                    '}'
                ].join('\n'),
                language: 'rust',
                theme: 'vs-dark'
            });
        });

        function saveAsFile(filename, content) {
            const blob = new Blob([content], { type: 'text/plain;charset=utf-8' });
            const link = document.createElement("a");
            link.href = URL.createObjectURL(blob);
            link.download = filename;
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        }

        document.getElementById('saveBtn').addEventListener('click', () => {
        const code = editor.getValue();
        saveAsFile("main.rs", code);
        });

【Python】editor

import curses

def main(stdscr):
    curses.curs_set(1)
    stdscr.clear()
    stdscr.refresh()

    text = []
    row = 0

    while True:
        stdscr.move(row, 0)
        stdscr.clrtoeol()
        stdscr.addstr(row, 0, ''.join(text))
        stdscr.refresh()
        ch = stdscr.getch()

        if ch == 27:  # ESCキーで終了
            break
        elif ch == 10:  # Enter
            text.append('\n')
            row += 1
        elif ch == 127 or ch == curses.KEY_BACKSPACE:
            if text:
                text.pop()
                if text[-1] == '\n':
                    row -= 1
        else:
            text.append(chr(ch))

    with open("output.txt", "w") as f:
        f.write(''.join(text))
    

curses.wrapper(main)

Rustで書くと

use crossterm::{
    cursor,
    event::{read, Event, KeyCode},
    terminal::{enable_raw_mode, disable_raw_mode, ClearType},
    ExecutableCommand,
};
use std::io::{stdout, Write};
use std::fs::File;

fn main() -> std::io::Result<()> {
    let mut stdout = stdout();
    let mut buffer = String::new();

    enable_raw_mode()?;
    stdout.execute(crossterm::terminal::Clear(ClearType::All))?;
    stdout.execute(cursor::MoveTo(0, 0))?;

    loop {
        match read()? {
            Event::Key(event) => match event.code {
                KeyCode::Char(c) => {
                    buffer.push(c);
                    print!("{}", c);
                    stdout.flush()?;
                }
                KeyCode::Enter => {
                    buffer.push('\n');
                    print!("\r\n");
                    stdout.flush()?;
                }
                KeyCode::Backspace => {
                    buffer.pop();
                    stdout.execute(cursor::MoveLeft(1))?;
                    print!(" ");
                    stdout.execute(cursor::MoveLeft(1))?;
                    stdout.flush()?;
                }
                KeyCode::Esc => {
                    break;
                }
                _ => {}
            },
            _ => {}
        }
    }

    disable_raw_mode()?;

    let mut file = File::create("output.txt")?;
    write!(file, "{}", buffer)?;

    Ok(())
}

inputファイルを読み込んで、出力

use crossterm::{
    cursor,
    event::{read, Event, KeyCode},
    terminal::{enable_raw_mode, disable_raw_mode, ClearType},
    ExecutableCommand,
};
use std::io::{stdout, Write};
use std::fs::{File, read_to_string};

fn main() -> std::io::Result<()> {
    let mut stdout = stdout();
    let mut buffer = String::new();

    let initial_text = match read_to_string("input.txt") {
        Ok(content) => content,
        Err(_) => String::new(),
    };
    buffer.push_str(&initial_text);

    enable_raw_mode().expect("Failed to enable raw mode");
    stdout.execute(crossterm::terminal::Clear(ClearType::All))?;
    stdout.execute(cursor::MoveTo(0, 0))?;

    let mut row: u16 = 0;
    let mut col: u16 = 0;
    for ch in buffer.chars() {
        match ch {
            '\n' => {
                print!("\r\n");
                row += 1;
                col = 0;
            }
            _ => {
                print!("{}", ch);
                col += 1;
            }
        }
    }
    stdout.flush()?;

    stdout.execute(cursor::MoveTo(col, row))?;

    loop {
        match read()? {
            Event::Key(event) => match event.code {
                KeyCode::Char(c) => {
                    buffer.push(c);
                    print!("{}", c);
                    stdout.flush()?;
                }
                KeyCode::Enter => {
                    buffer.push('\n');
                    row += 1;
                    print!("\r\n");
                    stdout.flush()?;
                }
                KeyCode::Backspace => {
                    buffer.pop();
                    stdout.execute(cursor::MoveLeft(1))?;
                    print!(" ");
                    stdout.execute(cursor::MoveLeft(1))?;
                    stdout.flush()?;
                }
                KeyCode::Esc => {
                    break;
                }
                _ => {}
            },
            _ => {}
        }
    }

    disable_raw_mode()?;

    let mut file = File::create("output.txt")?;
    write!(file, "{}", buffer)?;

    Ok(())
}

input.txtを行単位で読み込んで出力するなどの工夫が必要

【Rust】RwLockの使い所

複数スレッドで読み取りは同時、書き込みは排他


use std::sync::{Arc, RwLock};
use std::thread;

fn main() {
    let data = Arc::new(RwLock::new(5));

    let readers: Vec<_> = (0..3).map(|i| {
        let data = Arc::clone(&data);
        thread::spawn(move || {
            let read = data.read().unwrap();
            println!("Thread {} read: {}", i, *read);
        })
    }).collect();

    {
        let mut write = data.write().unwrap();
        *write += 1;
        println!("Main thread wrote: {}", *write);
    }

}

書き込みが多い場合はmutex向き

【Rust】Arcとはどういう時に使用するか?

Arc(std::sync::Arc)は、複数スレッドで安全に共有できる参照カウント付きスマートポインタ

### Arcはいつ使うか?
– 複数スレッドで同じデータを共有したい時
– かつ、読み取り専用 or 内部で排他制御する設計の時

use std::sync::Arc;
use std::thread;

fn main() {
    let data = Arc::new(vec![1, 2, 3]);

    let mut handles = vec![];

    for i in 0..3  {
        let data_clone = Arc::clone(&data);
        let handle = thread::spawn(move || {
            println!("Thread {} sees: {:?}", i, data_clone);
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }
}

$ ./playground
Thread 0 sees: [1, 2, 3]
Thread 1 sees: [1, 2, 3]
Thread 2 sees: [1, 2, 3]

同じようなコード

use std::sync::{Arc};
use std::thread;

fn main() {
    let data = Arc::new(1);

    let mut handles = vec![];

    for i in 0..3  {
        let data_clone = Arc::clone(&data);
        let handle = thread::spawn(move || {
            println!("{}", i + *data_clone);
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }
}

### 書き換えたい時

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let counter = Arc::new(Mutex::new(0));

    let mut handles = vec![];

    for _ in 0..10  {
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("Final counter: {}", *counter.lock().unwrap());
}

単なるデータのカウントアップだけなら、以下のようなシングルスレッドの方が効率的だが、

fn main() {
    let mut counter = 0;

    for _ in 0..10 {
        counter += 1;
    } 

    println!("{}", counter);
}

並列ダウンロード、Webサーバなどでは、圧倒的に複数スレッドの方が効率が良い。
それでArcやmutexが使用される。
なるほど〜

【Rust】box と dyn の使い所

Box は値をヒープに確保するためのスマートポインタ
L サイズがコンパイル時に決まらない型を使う時、dyn Traitを使う時、所有権を別の場所に移したいとき などに利用する

スタックではなく、ヒープに保存する

fn main() {
    let x = Box::new(77);
    println!("x = {}", x);
}

### 再起的なデータ構造で、コンパイル時にサイズが決まらないケース

#[derive(Debug)]
enum List {
    Cons(i32, Box<List>),
    Nil
}

use List::{Cons, Nil};

fn main() {
    let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
    println!("{:?}", list);
}

$ ./playground
Cons(1, Cons(2, Cons(3, Nil)))

### dyn Traitと一緒に使う

*/
trait Animal {
    fn speak(&self);
}

struct Dog;
impl Animal for Dog {
    fn speak(&self) {
        println!("Woof!");
    }
}

fn main() {
    let dog: Box<dyn Animal> = Box::new(Dog);
    dog.speak();
}

ヒープに確保せず、let dog = Dog; とした場合でも問題なく動くが、let dog の型を明示的に示す場合は、Boxと書くのね。。

つまり、
dyn Fn(Request) -> Box> + Send> + Send + Sync;
は、 dyn Fn(Request) は型が決まっていない Request という意味ですね。
Box<> は型が決まっていないのでヒープに保存。

【Rust】dyn って一体何か?

例えば、こんな一文があります。

type BoxedHandler = dyn Fn(Request<Body>) -> Box<dyn futures::Future<Output = Response<Body>> + Send> + Send + Sync;

typeがエイリアスなのはわかりますが、dynって一体なんでしょうか?
=>
dyn はRustにおける動的ディスパッチ(dynamic dispatch)を表すキーワードで、トレイトオブジェクトを使うときに必要
L どの型が来るかは実行時に決まる
L 複数の異なる型を一つのコレクションで扱いたい

trait Animal {
    fn speak(&self);
}

struct Dog;
struct Cat;

impl Animal for Dog {
    fn speak(&self) {
        println!("Woof!");
    }
}

impl Animal for Cat {
    fn speak(&self) {
        println!("Meow!");
    }
}

fn make_animal_noise(animal: &dyn Animal) {
    animal.speak();
}

fn main() {
    let dog = Dog;
    let cat = Cat;

    make_animal_noise(&dog);
    make_animal_noise(&cat);
}

うーん、わかったようなわかってないような…
別の例に置き換える

trait BulletTrain {
    fn run(&self);
}

struct TohokuShinkansen;
struct YamagataShinkansen;

impl BulletTrain for TohokuShinkansen {
    fn run(&self) {
        println!("東京-山形");
    }
}

impl BulletTrain for YamagataShinkansen {
    fn run(&self) {
        println!("東京-青森");
    }
}

fn travel_to_tohoku(bullettrain: &dyn BulletTrain) {
    bullettrain.run();
}

fn main() {
    let hayabusa1 = TohokuShinkansen;
    let tsubasa1 = YamagataShinkansen;

    travel_to_tohoku(&hayabusa1);
    travel_to_tohoku(&tsubasa1);
}

$ ./playground
東京-山形
東京-青森

【Rust】typeの使い所

型に別名/エイリアスを付けたい時に使用する

type Meters = u32;

fn print_distance(d: Meters) {
    println!("Distance: {} meters", d);
}

fn main() {
    let distance: Meters = 100;
    print_distance(distance);
}

$ ./playground
Distance: 100 meters

### よくある用途
複雑な型にエイリアスをつける

type Result<T> = std::result::Result<T, std::io::Error>;

fn read_file() -> Result<String> {
    std::fs::read_to_string("example.txt")
}

なるほど、複雑な型にエイリアスというのは納得!