Generics & Trait Bounds
TypeScript generics vs Rust generics
หัวข้อที่มีชื่อว่า “TypeScript generics vs Rust generics”ใน TypeScript คุณเขียน function identity<T>(x: T): T เพื่อให้ function ทำงานได้กับทุก type ส่วน Rust ใช้ syntax วงเล็บมุมแบบเดียวกัน: fn identity<T>(x: T) -> T ความต่างหลักคือ generic ของ TypeScript ถูก erase บางส่วนตอน compile time แล้วไปพึ่ง structural typing ส่วน Rust ทำ monomorphisation — compiler สร้าง function แบบ concrete แยกให้ทุก type ที่ใช้จริง ไม่มี overhead ตอน runtime
Trait bounds: การจำกัด T
หัวข้อที่มีชื่อว่า “Trait bounds: การจำกัด T”TypeScript จำกัด generic ด้วย extends: <T extends Printable> ส่วน Rust จำกัดด้วย trait bound: <T: Display> (inline syntax) หรือ where clause (แยกบรรทัด ใช้เมื่อ bound ยาว)
// TypeScript — constraint with extendsinterface Printable { toString(): string;}
function printTwice<T extends Printable>(value: T): void { console.log(value.toString()); console.log(value.toString());}
printTwice(42);printTwice("hello");use std::fmt::Display;
// Inline bound syntaxfn print_twice<T: Display>(value: T) { println!("{value}"); println!("{value}");}
// where clause — cleaner for multiple boundsfn print_and_debug<T>(value: T)where T: Display + std::fmt::Debug,{ println!("display: {value}"); println!("debug: {value:?}");}
fn main() { print_twice(42); print_twice("hello"); print_and_debug(3.14_f64);}Generic structs
หัวข้อที่มีชื่อว่า “Generic structs”เหมือนกับที่ TypeScript classes เป็น generic ได้ (class Stack<T>), Rust structs ก็เป็น generic ได้เช่นกัน คุณเพิ่ม type parameter หลังชื่อ struct และทำซ้ำใน impl block
// TypeScript generic classclass Stack<T> { private items: T[] = []; push(item: T): void { this.items.push(item); } pop(): T | undefined { return this.items.pop(); } peek(): T | undefined { return this.items[this.items.length - 1]; }}
const s = new Stack<number>();s.push(1);s.push(2);console.log(s.pop()); // 2struct Stack<T> { items: Vec<T>,}
impl<T> Stack<T> { fn new() -> Self { Stack { items: Vec::new() } } fn push(&mut self, item: T) { self.items.push(item); } fn pop(&mut self) -> Option<T> { self.items.pop() } fn peek(&self) -> Option<&T> { self.items.last() }}
fn main() { let mut s: Stack<i32> = Stack::new(); s.push(1); s.push(2); println!("{:?}", s.pop()); // Some(2)}Multiple trait bounds และ syntax +
หัวข้อที่มีชื่อว่า “Multiple trait bounds และ syntax +”คุณสามารถกำหนดให้ type ต้อง implement หลาย trait พร้อมกันโดยเชื่อมด้วย + เหมือนกับ T extends A & B ของ TypeScript
// TypeScriptfunction foo<T extends Serializable & Loggable>(x: T) { ... }
// Rust — inlinefn foo<T: Serialize + Debug>(x: T) { ... }
// Rust — where clause (แนะนำเมื่อ bounds ยาว)fn foo<T>(x: T)where T: Serialize + Debug + Clone,{ ... }ลองเล่นดู
หัวข้อที่มีชื่อว่า “ลองเล่นดู”use std::fmt::Display;
fn largest<T: PartialOrd>(list: &[T]) -> &T { let mut largest = &list[0]; for item in list { if item > largest { largest = item; } } largest}
fn describe<T: Display + std::fmt::Debug>(value: T) { println!("Display: {value}"); println!("Debug: {value:?}");}
fn main() { let numbers = vec![34, 50, 25, 100, 65]; println!("largest number: {}", largest(&numbers));
let chars = vec!['y', 'm', 'a', 'q']; println!("largest char: {}", largest(&chars));
describe(42_i32); describe("hello");}Compiling…