DEV Community

Gophernment
Gophernment

Posted on

Goish: พอร์ตรันไทม์ของ Go ทั้งตัวลงใน no_std Rust

📅 เขียนเมื่อ: สิงหาคม 2026 | Goish (goish.cogentica.ai)
⚠️ โปรเจกต์เพิ่งเปิดตัว — รายละเอียด/เวอร์ชันอาจเปลี่ยนแปลง ตรวจสอบจาก GitHub ก่อนใช้งาน

ถ้าคุณเขียน Go มาสักพัก คุณน่าจะเคยอยากได้สองอย่างพร้อมกัน — concurrency model ของ Go (goroutine, channel, select) กับ ความปลอดภัยด้าน memory ของ Rust (ownership, ไม่มี GC) ปกติแล้วสองอย่างนี้อยู่คนละโลก: Go ให้ runtime มาให้แต่มี garbage collector, Rust ให้ ownership มาแต่ concurrency ต้องประกอบเองจาก async/futures

มีคนพยายามเชื่อมสองโลกนี้เข้าด้วยกันแล้ว — ชื่อโปรเจกต์ว่า Goish ผลงานของ Chanwit Kaewkasi (@chanwit) วิศวกรชาวไทยจากโคราช ภายใต้บริษัท Cogentica AI


Goish คืออะไร

นิยามสั้น ๆ ที่เจ้าตัวเขียนไว้ในหน้าแรก: "A Rust runtime for Go people" — รันไทม์ของ Go สำหรับคนที่เขียน Go

ถ้าขยายให้ละเอียด: Goish คือการพอร์ต standard library และ runtime ของ Go 1.25 ลงไปใน Rust แบบ no_std — ไม่พึ่ง glibc, ไม่พึ่ง std ของ Rust, ไม่พึ่ง Tokio, ไม่มี garbage collector มัน ship ทุกอย่างมาเองตั้งแต่ _start, page allocator, size-class heap, M:N scheduler, channel, select!, sync primitives, ไปจนถึง net/http และ crypto/tls

ผลลัพธ์คือ binary ไฟล์เดียวแบบ static เหมือน Go — ldd รันแล้วบอกว่า "not a dynamic executable"

แต่สิ่งที่ทำให้ Goish ต่างจากโปรเจกต์ "เขียน Go ให้เป็น Rust" ทั่วไป คือวิธีที่มันให้เครดิตกับต้นทาง — เรื่องนี้แหละคือหัวใจของที่มาที่ไป


ทำไมถึงคิดทำตัวนี้ (ที่มาที่ไป)

แรงจูงใจหลักไม่ใช่ "อยากให้ Rust เขียนง่ายเหมือน Go" ธรรมดา ๆ แต่เป็นเรื่องของ compliance และ provenance

ลองนึกภาพ: ทีมคุณเขียน Go อยู่ แล้วต้องย้ายไป Rust (ด้วยเหตุผลเรื่อง memory safety หรือนโยบายบริษัท) ระหว่างทางคุณต้อง "พอร์ต" โค้ด — เอาโค้ดจาก Go มาเขียนใหม่เป็น Rust แล้วคำถามสำคัญก็ผุดขึ้น:

AES-GCM ใน binary ตัวนี้ เป็น implementation ที่ Go review แล้ว หรือเป็นของที่เราเขียนประมาณเอา?

เครื่องมือ supply-chain ยุคปัจจุบันอย่าง SLSA provenance หรือ SBOM ตอบคำถามนี้ไม่ได้ — มันอธิบายแค่ว่า binary ถูก build มาจากไหน (builder ไหน, commit อะไร, digest อะไร) แต่อธิบายไม่ได้ว่าโค้ดข้างในถูก port มาอย่างซื่อสัตย์แค่ไหน SBOM จะบันทึกว่า "goish 0.1.0" แล้วจบ — มันแยกไม่ออกว่า crypto ในนั้นคือของจริงหรือของก๊อป

Goish แก้ด้วยการบันทึกที่มาเป็นฟังก์ชันต่อฟังก์ชัน:

// go: sdk 1.25.5 crypto/internal/fips140/aes/gcm/gcm.go:31-46 newGCM
Enter fullscreen mode Exit fullscreen mode

บรรทัดนี้เขียนไว้เหนือ port แต่ละตัว — บอกว่าโค้ดตรงนี้ port มาจากไฟล์ไหน บรรทัดไหน ใน Go 1.25.5 และมี CI ที่เปิด Go tree ขึ้นมาเช็คทุก commit ว่าตำแหน่งที่อ้างยังถูกต้องอยู่

ทำไมเรื่องนี้ถึงสำคัญตอนนี้? เพราะเส้นตายทางกฎหมายกำลังมา:

วันที่ เกิดอะไรขึ้น
11 ก.ย. 2026 EU CRA (Cyber Resilience Act) บังคับใช้ภาระการรายงาน — แจ้งเตือน 24 ชม. / แจ้งเต็ม 72 ชม. เมื่อเจอช่องโหว่ที่ถูกโจมตีจริง
21 ก.ย. 2026 ใบรับรอง FIPS 140-2 ย้ายเข้า "Historical List" — ใช้ต่อได้แต่ justify การจัดซื้อภาครัฐใหม่ไม่ได้
1 ม.ค. 2027 CNSA 2.0 กลายเป็นค่าเริ่มต้นสำหรับงาน NSS ของสหรัฐฯ
11 ธ.ค. 2027 ข้อกำหนดสำคัญของ CRA รวมถึง mandate เรื่อง machine-readable SBOM

พูดง่าย ๆ: ถ้าสินค้าคุณต้องผ่าน compliance พวกนี้ คุณต้องพิสูจน์ได้ว่าโค้ดทุกบรรทัดมาจากไหน — และสำหรับโค้ดที่ "แปล" มาจากที่อื่น การพิสูจน์นั้นต้องลงถึงระดับฟังก์ชัน ไม่ใช่ระดับ build

นี่คือเหตุผลที่ Goish เกิด


ใช้เทคนิคอะไรบ้าง

ส่วนนี้คือความเจ๋งทางวิศวกรรม — ถ้าไม่อยากอ่านเชิงลึก ข้ามไปหัวข้อถัดไปได้เลย

1. M:N Scheduler — พอร์ตแบบ verbatim จาก runtime/proc.go

Goish ไม่ได้ "เขียน scheduler ใหม่ให้คล้าย Go" — มันแปล runtime/proc.go ของ Go 1.25 มาทีละบรรทัด:

  • Per-P run queue แบบ lock-free SPMC (256 entry) + global overflow
  • Work stealing แบบ coprime-permuted (runqgrab / runqsteal / stealOrder)
  • Async preemption ผ่าน SIGURG (เทคนิคเดียวกับที่ Go ใช้หยุด goroutine ที่รันนานเกิน)
  • sysmon ตัวจัดการ timer heap + force-preempt
  • epoll netpoller แบบ per-P (โมเดลเดียวกับ nginx) — เวลา goroutine รอ I/O มันจะ park goroutine ไม่ใช่ block thread

2. Memory: page allocator + 67 size classes (แต่ไม่มี GC)

Goish เอาโครงสร้าง allocator ของ Go (mheap → mcentral → per-P mcache, 67 size classes) มาใช้ แต่ตัด garbage collector ออก — ให้ Rust ownership เป็นคน reclaim แทน

สำหรับ stack ของ goroutine มันใช้วิธีฉลาด: goroutine หนึ่งตัวได้ virtual reservation 1 MiB แบบ lazy-commit (kernel ค่อย commit หน้า 4 KiB เมื่อถูกแตะจริง) — แปลว่า goroutine ที่ทำงานตื้น ๆ เสีย memory จริงแค่ประมาณหนึ่ง page และถ้าอยากได้ความหนาแน่นสุดขีด ก็ใช้ go!(stack(2*KB), …) เพื่อขอ stack ขนาด 2 KiB จาก stack pool

ผลลัพธ์ตาม demo:

ts  vmsize_kb  vmrss_kb  vmpeak_kb  vmhwm_kb  threads
 0s    1105148      44800    1108444      49024   13   ← baseline
 2s    3069660    2406528    3069660    2406528   13   ← 1M parked
30s    3069660    2406528    3069660    2406528   13   ← steady-state
Enter fullscreen mode Exit fullscreen mode

goroutine หนึ่งล้านตัว บน OS thread แค่ 13 เธรด — ~2.4 KiB RSS ต่อ goroutine

3. เขียนโค้ดที่ "อ่านแล้วเหมือน Go" แต่ compile ด้วย rustc

จุดเด่นที่สุดสำหรับคนเขียน Go — public API ใช้ type ตัวพิมพ์เล็กแบบ Go แทน type ของ Rust:

fn get(url: string) -> (int, string, string) {
    let (mut resp, err) = http::Get(url.clone());
    if err != nil {
        return (-1, fmt::Sprintf!("get %s: %v", url, err), string(""));
    }
    let (body, _) = io::ReadAll(&mut resp.Body);
    let _ = io::Closer::Close(&mut resp.Body);
    return (resp.StatusCode, string(body), resp.Header.Get("Content-Type"));
}
Enter fullscreen mode Exit fullscreen mode

string, int, multi-return, if err != nil — ทั้งหมดคือ Rust จริง ๆ ที่ rustc compile ได้ แต่อ่านแล้วแทบไม่ต่างจาก Go และ Vec<u8> กับ &str ไม่โผล่ใน signature สาธารณะเลย

นี่คือ philosophy หลัก: "Port first, get idiomatic later" — พอร์ตโค้ด Go มาแบบเกือบ line-by-line ก่อน แล้วค่อย ๆ ปรับเป็น Rust idiom ทีละ module เมื่อทีมพร้อม

4. crypto/tls — พอร์ตครบ 100% พร้อมใบเสร็จทุกบรรทัด

ส่วนที่ภาคภูมิใจที่สุดคือ crypto/ครบ 1722/1722 declarations (100%) ทั้ง 66 packages รวมถึง crypto/internal/fips140* ทั้ง 35 packages ที่เป็น code path ของ FIPS validation และ net/http ครบ 639/639 functions (100%)

TLS 1.3 ที่รันจริงคือ crypto/tls ที่ port มา verbatim — ไม่ใช่ implementation ใหม่ที่เขียนเลียนแบบ


ประโยชน์ที่ได้

  1. Binary เดียว ไม่มี libc ไม่มี ld.so — ลด attack surface เหลือแค่ตัว binary เอง เหมาะกับ scratch/distroless container, confidential VM, appliance image
  2. Memory safety + concurrency model ของ Go — ได้ ownership ของ Rust (ไม่มี dangling pointer, ไม่มี data race) กับ goroutine/channel/select ของ Go ในตัวเดียว
  3. Provenance ถึงระดับฟังก์ชัน — พิสูจน์ compliance (EU CRA, FIPS, CNSA) ได้ เพราะทุกฟังก์ชันมีที่มาอ้างอิง
  4. ไม่มี GC ให้ tune — ไม่มี stop-the-world ไม่มี GC pause
  5. เส้นทาง migrate จาก Go → Rust แบบค่อยเป็นค่อยไป — ไม่ต้องหยุดทุกอย่างเพื่อ rewrite ใหม่หมด

วิธีเริ่มต้น

ต้องการแค่ Rust 1.79+ และ Linux x86_64 (single-target ณ ตอนนี้)

git clone https://github.com/cogentica-ai/goish
cd goish

# build library
cargo build --target x86_64-unknown-linux-gnu --release

# demo: หนึ่งล้าน goroutine
cargo build --target x86_64-unknown-linux-gnu --release --example spawn_million
./examples/spawn_million.sh

# goginx: nginx clone ที่เขียนด้วย goish
cargo build --target x86_64-unknown-linux-gnu --release --example goginx
Enter fullscreen mode Exit fullscreen mode

โค้ด "Hello" แบบ Goish หน้าตาประมาณนี้:

use goish::{fmt, go};

#[goish::main]
fn main() {
    go!(move || {
        fmt::Println!("hello from a goroutine");
    });
}
Enter fullscreen mode Exit fullscreen mode

ข้อจำกัด (บอกตรง ๆ)

  • ยังไม่ใช่ Go compiler — คุณเขียน Rust ที่อ่านเหมือน Go ไม่ได้ compile ไฟล์ .go
  • Linux x86_64 เท่านั้น — target อื่นยังไม่อยู่ใน scope
  • ยังไม่ผ่าน security audit — TLS stack port มาแบบ machine-checked แต่ยังไม่มี external review (ดู SECURITY.md ก่อนใช้จริง)
  • ยัง port ไม่ครบทั้ง Gocrypto/ กับ net/http เสร็จแล้ว แต่ net, encoding, os ยังบางส่วน

สรุป

Goish ไม่ได้เป็นแค่ "Go runtime ใน Rust" — มันเป็นความพยายามตอบคำถามที่ใหญ่กว่านั้น: เมื่อเราต้องย้ายภาษาในโลกที่มีกฎหมายบังคับเรื่องที่มาของโค้ด เราจะพิสูจน์ได้ยังไงว่าโค้ดทุกบรรทัดไว้ใจได้

คำตอบของ Chanwit คือการพอร์ต Go แบบมี "ใบเสร็จ" ทุกบรรทัด แล้วเอา ownership ของ Rust มาแทน GC — ได้ทั้งความคุ้นเคยของ Go, ความปลอดภัยของ Rust, และหลักฐาน compliance ที่ตรวจซ้ำได้

โปรเจกต์นี้เพิ่งเปิด (สิงหาคม 2026) และยังพัฒนาอย่างต่อเนื่อง ถ้าสนใจเข้าไปดูได้ที่:


เครดิต: บทความนี้อ้างอิงข้อมูลจากเว็บไซต์ goish.cogentica.ai และ README ของ github.com/cogentica-ai/goish — โปรเจกต์โดย Chanwit Kaewkasi (@chanwit) ภายใต้ Cogentica AI (ประเทศไทย)

⚠️ Goish ไม่เกี่ยวข้องกับ Google หรือ Go project — "Go" เป็นเครื่องหมายการค้าของ Google LLC

Top comments (0)