Halo Bro! Pernah denger bahasa pemrograman yang katanya bakal ngegeser C++? Yang compiler-nya galak banget tapi bikin kode lu aman dari bug memori? Kenalin: Rust.
Tutorial Lengkap Belajar Rust: Dari Dasar, Borrow Checker, Hingga WebAssembly (Wasm)
Tapi sebelum kita ngomongin multithreading tingkat dewa atau ngehindarin race conditions, kita harus pedekate dulu sama cara instalasinya ada 4 jurus . Gass!
Jurus 1: Panggil "Mbahnya" Rust (Instalasi)
Lu nggak perlu ribet download file zip sana-sini. Kalau lu pakai Windows, Linux, atau Mac, lu tinggal buka terminal/CMD dan ketik mantra sakti ini:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Itu namanya rustup. Dia yang bakal ngurusin semua tools Rust di PC lu. Kalau udah kelar, restart terminal lu, terus cek apakah Rust udah nongkrong di PC lu dengan ketik:
rustc --version
Kalau muncul angkanya, congrats, lu udah resmi jadi Rustacean (sebutan buat anak-anak Rust)!
Jurus 2: Bikin Proyek Pertama Pakai Cargo
Nah, Rust ini punya asisten pribadi namanya Cargo. Dia ini package manager sekaligus build system. Jadi kalau mau bikin proyek baru, lu nggak usah bikin folder manual. Tinggal suruh Cargo:
cargo new app_laundry_cli
cd app_laundry_cli
Kenapa namanya app_laundry_cli? Iseng aja Bro, kita bikin simulasi program kasir sederhana via terminal buat Part 1 ini. Kalau lu buka folder-nya, Cargo udah bikinin struktur file yang rapi. Lu tinggal buka file src/main.rs.
Jurus 3: Script Tutorial (Mari Kita Ngoding!)
Hapus semua isi di main.rs, terus copy-paste script di bawah ini. Tenang, gue kasih komen bahasa manusia biar gampang dipahamin:
use std::io; // Kita pinjem alat bawaan Rust buat nerima input dari keyboard
fn main() {
println!("=====================================");
println!("๐ฅ Selamat Datang di Kasir Rust ๐ฅ");
println!("=====================================");
println!("Bro, masukin nama pelanggan hari ini:");
// Bikin variabel 'String' kosong buat nampung nama
// 'mut' artinya mutable (nilainya boleh diubah-ubah nanti)
let mut nama_pelanggan = String::new();
// Disini kita baca ketikan keyboard lu, terus dimasukin ke variabel tadi
io::stdin()
.read_line(&mut nama_pelanggan)
.expect("Waduh, gagal baca input nih Bro!");
// Nah, .trim() fungsinya buat ngebuang spasi kosong atau enter di akhir kata
let nama = nama_pelanggan.trim();
println!("-------------------------------------");
// Di Rust, kalau mau masukin variabel ke teks, tinggal pake kurung kurawal {}
println!("Halo, Bos {}! Cucian udah siap diproses.", nama);
println!("Sistem aman, memori nggak bocor, gass terus!");
}
Jurus 4: Jalanin Aplikasinya (Simulasi Hasil)
Gimana cara liat hasilnya? Gampang banget. Lu nggak perlu compile manual. Di dalam folder proyek tadi, ketik aja ini di terminal lu:
cargo run
Hasilnya di terminal lu bakal kayak gini nih:
๐ฅ Selamat Datang di Kasir Rust ๐ฅ
Bro, masukin nama pelanggan hari ini:
Raja Digital (lu ngetik ini terus enter)
Halo, Bos Raja Digital! Cucian udah siap diproses.
Sistem aman, memori nggak bocor, gass terus!
Keren kan? Kode lu langsung diubah jadi machine code yang super ngebut.
Di bagian selanjutnya, kita bakal mulai mainan yang namanya Borrow Checker—fitur nyebelin tapi bikin kangen yang bikin Rust dijulukin "Keamanan Tanpa Tanding". Pantengin terus blog ini ya Bro. Jangan lupa seruput kopinya! ☕
Lanjut gass, Bro! Kali ini gua bakal main-main sedikit lebih dalam. Sebelomnya udah bikin program kasir yang cuma bisa nanya nama doang. Sekarang, kita bikin aplikasinya mikir dikit.
Kita bakal bahas Variabel, Tipe Data, dan Array, tapi biar nggak bosen, simulasi programnya kita ganti jadi aplikasi kalkulator bahan baku. Siapa tahu bisa bantu hitung-hitungan kalau mau rilis produk sabun cuci piring komersial ke marketplace.
Ngajarin Rust Berhitung: Bikin Kalkulator Bahan Baku Sabun
Di Rust, ada satu aturan fundamental yang wajib lu hafal mati: Secara default, semua variabel di Rust itu kekunci (Immutable).
Artinya apa? Kalau lu naruh air di gelas, airnya nggak bisa lu ganti jadi kopi. Kecuali dari awal lu udah bilang ke Rust, "Eh, ini gelasnya khusus buat gonta-ganti minuman ya!" (alias Mutable). Aturan ini sengaja dibikin biar kode lu aman dari bug pas lagi ngejalanin multithreading tingkat dewa nanti.
Biar gampang ngebayanginnya, ayo kita bikin program buat ngelist dan ngitung kebutuhan bahan kimia dasar. Buka lagi file src/main.rs lu, hapus kode yang kemarin, dan masukin kode ini:
Script Tutorial: Kalkulator Bahan Baku
fn main() {
println!("===================================================");
println!("๐งช Kalkulator Resep Sabun Cuci Piring ๐งช");
println!("===================================================");
// 1. ARRAY (Ngelist Bahan)
// Ingat, array di Rust ukurannya tetap. Kita masukin 3 bahan utama.
let bahan_baku = ["MES", "Texapon", "LABS"];
println!("Bahan utama yang kita pakai hari ini:");
println!("1. {}", bahan_baku[0]);
println!("2. {}", bahan_baku[1]);
println!("3. {}", bahan_baku[2]);
println!("---------------------------------------------------");
// 2. VARIABEL IMMUTABLE (Nilai Tetap)
// Tipe data 'f64' itu buat angka desimal (pecahan).
let target_produksi_liter: f64 = 50.0;
// 3. VARIABEL MUTABLE (Nilai Bisa Berubah)
// Pakai kata 'mut' karena stok awal nol, terus nanti mau kita isi nilainya.
let mut stok_texapon_kg: f64 = 0.0;
// Kita ubah nilai variabel mutable-nya (misal butuh 5% dari target produksi)
stok_texapon_kg = target_produksi_liter * 0.05;
println!("Target Produksi: {} Liter", target_produksi_liter);
println!("Kebutuhan {} yang harus disiapin: {} Kg", bahan_baku[1], stok_texapon_kg);
println!("===================================================");
println!("Sistem kalkulasi sukses. Nggak ada memori yang bocor!");
}
Penjelasan :
["MES", "Texapon", "LABS"]: Ini namanya Array. Cara ngambil datanya pakai angka indeks yang dimulai dari 0. Jadibahan_baku[1]itu manggil "Texapon".let target_produksi_liter:Nggak pakai mut, berarti angkanya dikunci selamanya di angka 50. Kalau lu iseng nambahin kodetarget_produksi_liter = 100.0;di bawahnya, compiler Rust bakal ngamuk dan program lu gagal jalan.let mut stok_texapon_kg:Nah, kalau ada katamut, baru deh lu bisa gonta-ganti isinya sesuka hati.
Hasil Aplikasinya (Simulasi Terminal)
Tinggal ketik cargo run lagi di terminal lu, dan boom! Ini hasilnya:
๐งช Kalkulator Resep Sabun Cuci Piring ๐งช
Bahan utama yang kita pakai hari ini:
1. MES
2. Texapon
3. LABS
---------------------------------------------------
Target Produksi: 50 Liter
Kebutuhan Texapon yang harus disiapin: 2.5 Kg
===================================================
Sistem kalkulasi sukses. Nggak ada memori yang bocor!
Misteri "Borrow Checker": Satpam Memori yang Bikin Aplikasi Lu Anti-Crash
Di bahasa seperti C++, lu bebas bikin dan hapus data di memori. Efeknya? Sering kejadian yang namanya Memory Leak (memori bocor) atau program tiba-tiba crash (Segfault). Di bahasa seperti Java atau JavaScript, ada "petugas kebersihan" otomatis yang namanya Garbage Collector. Aman sih, tapi bikin aplikasi jadi lebih lambat dan makan resource.
Nah, Rust ngambil jalan ninja yang beda. Dia nggak pakai Garbage Collector, tapi punya aturan super ketat yang dicek langsung pas lu ngetik kode. Aturan ini dijaga sama "satpam" galak bernama Borrow Checker.
Biar gampang paham, kita pakai Analogi Bisnis Laundry aja ya:
- Ownership (Kepemilikan): Setiap baju (data) pasti cuma punya SATU pemilik (owner). Kalau bajunya dipindahin ke orang lain, pemilik lama udah nggak berhak lagi ngurusin baju itu.
- Borrowing (Peminjaman): Kalau karyawan laundry mau ngecek noda di baju, dia cuma minjem buat dilihat (Immutable Borrowing). Banyak karyawan boleh ngelihat baju itu barengan. Tapi, kalau karyawan mau nyetrika atau nyuci (Mutable Borrowing), cuma boleh SATU orang yang pegang bajunya di satu waktu. Nggak boleh rebutan!
Script Tutorial: Belajar Minjem Data
fn main() {
println!("=======================================");
println!("๐ต️♂️ Latihan Borrow Checker Rust ๐ต️♂️");
println!("=======================================\n");
// 1. CONTOH OWNERSHIP (Kepemilikan)
let pelanggan_a = String::from("Raja Digital");
// Data dipindah (di-Move) ke pelanggan_b.
// Sekarang pelanggan_a udah 'kosong' dan nggak valid lagi!
let pelanggan_b = pelanggan_a;
// Kalau lu iseng nulis: println!("{}", pelanggan_a);
// Compiler Rust bakal ngamuk dan kode lu GAGAL jalan.
println!("Data sekarang dipegang oleh: {}", pelanggan_b);
println!("---------------------------------------");
// 2. CONTOH BORROWING (Peminjaman dengan tanda &)
let nota_cucian = String::from("Cuci Kilat 5 Kg");
// Kita pakai tanda & (Ampersand) buat 'minjem' data tanpa ngambil hak miliknya.
let karyawan_1 = ¬a_cucian;
let karyawan_2 = ¬a_cucian;
// Karena cuma minjem buat DIBACA, dua karyawan bisa akses barengan. Aman!
println!("Karyawan 1 baca: {}", karyawan_1);
println!("Karyawan 2 baca: {}", karyawan_2);
// Pemilik asli (nota_cucian) juga masih bisa dipakai karena hak miliknya nggak hilang.
println!("Pemilik asli datanya tetap: {}", nota_cucian);
println!("=======================================");
println!("Lulus ujian satpam memori! Gass lanjut!");
}
Penjelasan Santai:
String::from(...): Ini cara kita bikin teks yang disimpen di memori Heap (memori dinamis).- Konsep Move: Saat kita nulis
let pelanggan_b = pelanggan_a;, Rust langsung matiin akses pelanggan_a buat nyegah Double Free Error. - Tanda & (Ampersand): Ini kunci utamanya. Kalau lu liat tanda &, artinya lu cuma ngasih akses "numpang baca". Ini bikin memori lu sangat efisien.
Hasil Aplikasinya (Simulasi Terminal)
๐ต️♂️ Latihan Borrow Checker Rust ๐ต️♂️
Data sekarang dipegang oleh: Raja Digital
---------------------------------------
Karyawan 1 baca: Cuci Kilat 5 Kg
Karyawan 2 baca: Cuci Kilat 5 Kg
Pemilik asli datanya tetap: Cuci Kilat 5 Kg
=======================================
Lulus ujian satpam memori! Gass lanjut!
Bikin "Blueprint" Data Pakai Struct: Manajemen Orderan Nggak Pake Ribet
Bayangin lu lagi bikin sistem backend buat bisnis laundry. Lu butuh nyimpen data: Nama Pelanggan, Berat Cucian, dan Jenis Layanan. Kalau lu pakai variabel misah-misah buat tiap pelanggan, kode lu bakal panjang banget.
Solusinya? Kita bungkus semua data itu pakai Struct.
Script Tutorial: Bikin Sistem Pencatatan Order
// 1. KITA BIKIN BLUEPRINT-NYA DULU (Struct)
struct PesananLaundry {
id_order: u32,
nama_pelanggan: String,
berat_kg: f64,
layanan_kilat: bool, // true kalau kilat, false kalau reguler
}
// 2. KITA BIKIN FUNGSI KHUSUS BUAT STRUCT INI (Impl)
impl PesananLaundry {
fn cetak_struk(&self) {
println!("----------------------------------");
println!("๐งพ STRUK LAUNDRY - ORDER #{}", self.id_order);
println!("----------------------------------");
println!("Pelanggan : {}", self.nama_pelanggan);
println!("Berat : {} Kg", self.berat_kg);
if self.layanan_kilat {
println!("Layanan : EXPRESS ⚡ (Besok Jadi)");
} else {
println!("Layanan : REGULER ๐ข (Santai Aja)");
}
println!("----------------------------------\n");
}
}
fn main() {
println!("==================================");
println!("Sistem Manajemen Raja Digital ID");
println!("==================================\n");
// 3. KITA ISI FORMULIRNYA (Bikin Instance dari Struct)
let orderan_pertama = PesananLaundry {
id_order: 101,
nama_pelanggan: "Si Bos Kopi".to_string(),
berat_kg: 3.5,
layanan_kilat: true,
};
let orderan_kedua = PesananLaundry {
id_order: 102,
nama_pelanggan: "Anak Kosan".to_string(),
berat_kg: 5.0,
layanan_kilat: false,
};
// 4. JALANIN FUNGSINYA
orderan_pertama.cetak_struk();
orderan_kedua.cetak_struk();
}
Sistem Manajemen Raja Digital ID
==================================
----------------------------------
๐งพ STRUK LAUNDRY - ORDER #101
----------------------------------
Pelanggan : Si Bos Kopi
Berat : 3.5 Kg
Layanan : EXPRESS ⚡ (Besok Jadi)
----------------------------------
----------------------------------
๐งพ STRUK LAUNDRY - ORDER #102
----------------------------------
Pelanggan : Anak Kosan
Berat : 5 Kg
Layanan : REGULER ๐ข (Santai Aja)
----------------------------------
Multithreading "Dewa": Ngerjain Banyak Tugas Barengan Tanpa Takut Race Condition
Kalau lu pengen bikin aplikasi backend super ngebut, atau mau ngoptimalin sistem web biar bisa ngelayanin ribuan request dalam satu detik tanpa ngelag, lu wajib paham ilmu ini.
Di Rust, lu bakal kenalan sama senjata andalan kita: Mutex (Mutual Exclusion).
Script Tutorial: Simulasi Pengunjung Web dengan Mutex
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
fn main() {
println!("=============================================");
println!("๐ Simulasi Multithreading Raja Digital ID ๐");
println!("=============================================\n");
// 1. SIAPIN DATA DAN MUTEX
let total_pengunjung = Arc::new(Mutex::new(0));
let mut kumpulan_thread = vec![];
println!("Mulai ngebuka 10 pintu masuk buat pengunjung...");
for i in 1..=10 {
let tiket_masuk = Arc::clone(&total_pengunjung);
let tugas = thread::spawn(move || {
thread::sleep(Duration::from_millis(10));
let mut data_dikunci = tiket_masuk.lock().unwrap();
*data_dikunci += 1;
println!("Thread {} berhasil masuk! Total pengunjung sekarang: {}", i, *data_dikunci);
});
kumpulan_thread.push(tugas);
}
for tugas in kumpulan_thread {
tugas.join().unwrap();
}
println!("\n=============================================");
println!("Semua tugas selesai!");
println!("Total Pengunjung Final: {}", *total_pengunjung.lock().unwrap());
}
Selamat Tinggal "Kutukan Null": Cara Rust Nanganin Error
Di Rust, kata "Null" itu nggak ada. Nggak eksis, Bro! Sebagai gantinya, Rust punya dua senjata pamungkas buat nanganin data kosong atau error: Option dan Result.
Script Tutorial: Sistem Pengecekan Saldo & Pencarian Pelanggan
fn main() {
let nama_dicari = "Anak Kosan";
println!("๐ Mencari data '{}'...", nama_dicari);
match cari_pelanggan(nama_dicari) {
Some(nama) => println!("✅ Ketemu nih: Bos {}", nama),
None => println!("❌ Waduh, data pelanggan '{}' nggak ada di sistem!", nama_dicari),
}
let saldo_dompet = 50000;
let tagihan_laundry = 75000;
println!("๐ธ Coba bayar tagihan Rp{} dengan saldo Rp{}...", tagihan_laundry, saldo_dompet);
match proses_pembayaran(saldo_dompet, tagihan_laundry) {
Ok(kembalian) => println!("✅ Pembayaran sukses! Kembalian lu: Rp{}", kembalian),
Err(pesan_error) => println!("❌ Transaksi Ditolak: {}", pesan_error),
}
}
fn cari_pelanggan<'a>(nama_target: &'a str) -> Option<&'a str> {
let database = ["Si Bos Kopi", "Raja Digital", "Juragan Laundry"];
for &pelanggan in database.iter() {
if pelanggan == nama_target {
return Some(pelanggan);
}
}
None
}
fn proses_pembayaran(saldo: i32, tagihan: i32) -> Result<i32, String> {
if saldo >= tagihan {
Ok(saldo - tagihan)
} else {
Err(String::from("Saldo lu kurang Bro, ngutang dulu gih!"))
}
}
Array Kekecilan? Kenalan Sama Vector & HashMap!
Array itu ibarat rak sepatu yang jumlah slotnya udah dipatenkin dari awal. Gimana kalau tiba-tiba orderan meledak dan lu butuh nyimpen 100 data baru? Array bakal nyerah, Bro.
Makanya, Rust ngasih kita dua struktur data yang paling sering dipake di dunia nyata: Vector dan HashMap.
Script Tutorial: Sistem Keranjang Belanja Dinamis
use std::collections::HashMap;
fn main() {
// 1. VECTOR (Daftar Belanjaan yang bisa nambah terus)
let mut keranjang_belanja: Vec<String> = Vec::new();
keranjang_belanja.push(String::from("Sabun Cuci Piring Jeruk Nipis 1L"));
keranjang_belanja.push(String::from("Bahan MES 1Kg"));
// 2. HASHMAP (Daftar Harga pakai sistem Key-Value)
let mut daftar_harga = HashMap::new();
daftar_harga.insert("Sabun Cuci Piring Jeruk Nipis 1L", 15000);
daftar_harga.insert("Bahan MES 1Kg", 25000);
// 3. MENGGABUNGKAN KEDUANYA
let mut total_bayar = 0;
for barang in &keranjang_belanja {
match daftar_harga.get(barang.as_str()) {
Some(harga) => {
println!("- {} : Rp{}", barang, harga);
total_bayar += harga;
},
None => println!("- {} : Harga belum diatur!", barang),
}
}
}
Bikin Sistem Lacak Resi Ala Shopee Pakai "Enum"
Nah, Enum ini fungsinya buat mendaftar semua kemungkinan yang valid. Lu ngebatasin pilihan biar sistem nggak nerima input ngawur. Asyiknya lagi di Rust, Enum bisa diselipin data tambahan, kayak nomor resi kurir!
enum StatusPesanan {
MenungguPembayaran,
SedangDikemas,
SedangDikirim(String), // Enum bawa data resi!
Selesai,
}
struct Orderan {
id_order: u32,
nama_pembeli: String,
status: StatusPesanan,
}
fn cek_posisi_paket(order: &Orderan) {
match &order.status {
StatusPesanan::MenungguPembayaran => println!("Status: ⏳ Woi Bro, buruan transfer!"),
StatusPesanan::SedangDikirim(resi) => println!("Status: ๐ Udah jalan. Resi: {}", resi),
_ => println!("Status Lainnya..."),
}
}
Tinggalkan Terminal! Bikin Web Server Super Ngebut Pakai Rust
Buat lu yang sering ngoprek web dan ngejar skor PageSpeed Insights 100/100, pasti tahu kalau performa backend (server) itu ngaruh banget ke waktu loading (TTFB). Kita bakal minjem framework sakti bernama Actix-Web.
Jurus 1: Tambahin "Bumbu" di Cargo.toml
[dependencies]
actix-web = "4.0"
Jurus 2: Script Tutorial (Bikin Servernya)
use actix_web::{get, App, HttpResponse, HttpServer, Responder};
#[get("/")]
async fn halaman_utama() -> impl Responder {
HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body("<h1>๐ Halo Bro! Server Rust Udah Nyala!</h1>")
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new().service(halaman_utama)
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
Bikin API Kasir & Tampil Data JSON: Saatnya Web Lu Ngobrol Sama Sistem!
Biasanya, backend dibikin cuma buat nyediain API. Nanti urusan nampilin visual webnya diserahin ke frontend yang lu build pakai HTML/JS, biar skor PageSpeed lu tetep 100/100 tanpa beban server yang berat.
[dependencies]
actix-web = "4.0"
serde = { version = "1.0", features = ["derive"] }
use actix_web::{get, web, App, HttpResponse, HttpServer, Responder};
use serde::{Deserialize, Serialize};
use std::sync::Mutex;
#[derive(Serialize, Deserialize, Clone)]
struct Produk { id: u32, nama: String, harga: u32, stok: u32 }
struct AppState { db_produk: Mutex<Vec<Produk>> }
#[get("/api/produk")]
async fn get_produk(data: web::Data<AppState>) -> impl Responder {
let database = data.db_produk.lock().unwrap();
HttpResponse::Ok().json(&*database) // Langsung jadi JSON!
}
Kawin Silang Rust & HTML: Nampilin Data API ke Web Frontend
Biar API lu bisa dipanggil dari domain web lain, kita harus ngebuka gerbang yang namanya CORS di server Rust kita.
<!DOCTYPE html>
<html lang="id">
<head>
<title>Sistem Kasir Frontend</title>
</head>
<body>
<h1>๐ฆ Data Stok Gudang</h1>
<button onclick="tarikDataAPI()">๐ Segarkan Data JSON</button>
<div id="etalase-produk"></div>
<script>
async function tarikDataAPI() {
let response = await fetch("http://127.0.0.1:8080/api/produk");
let dataJSON = await response.json();
console.log(dataJSON);
}
</script>
</body>
</html>
AJIAN SANTET: Bawa Rust ke Browser Pakai WebAssembly (Wasm)!
Dengan Wasm, lu bisa ngubah kode Rust jadi format binary yang bisa dibaca sama Chrome, Safari, atau Firefox. Eksekusinya hardware-accelerated dan hampir secepat aplikasi PC (native). Skor PageSpeed Insights lu bakal melenggang mulus di 100/100!
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn hitung_diskon_grosir(jumlah_botol: i32, harga_satuan: i32) -> String {
let total_awal = jumlah_botol * harga_satuan;
let mut diskon = 0;
if jumlah_botol >= 100 {
diskon = total_awal * 20 / 100; // Diskon 20%
}
format!("Total Bayar: Rp{}", total_awal - diskon)
}
wasm-pack build --target web
Trik PageSpeed 100/100: Bikin Script Auto-Kompres Gambar Pakai Rust!
Kalau ngomongin soal optimasi web, ukuran gambar itu sering jadi biang kerok yang bikin skor PageSpeed anjlok. Kita bakal bikin program CLI sederhana pakai Rust buat nge-resize gambar mentah.
use image::imageops::FilterType;
use std::time::Instant;
fn main() {
let waktu_mulai = Instant::now();
let gambar_mentah = image::open("sabun_raw.jpg").expect("File ilang!");
let (lebar_asli, tinggi_asli) = gambar_mentah.dimensions();
let target_lebar = 800;
let target_tinggi = (tinggi_asli * target_lebar) / lebar_asli;
let gambar_kecil = gambar_mentah.resize(target_lebar, target_tinggi, FilterType::Lanczos3);
gambar_kecil.save("sabun_optimized.jpg").expect("Gagal simpan!");
println!("✅ Beres Bro! Dieksekusi dalam waktu {:?}", waktu_mulai.elapsed());
}
๐ผ️ Mesin Optimasi Gambar Raja Digital ID ๐ผ️
⏳ Lagi ngebaca file: sabun_raw.jpg...
✂️ Lagi nge-resize jadi: 800 x 600 pixels...
๐พ Nyimpen hasil optimasi ke: sabun_optimized.jpg...
✅ Beres Bro! Semua dieksekusi dalam waktu 142.5ms
Gambar udah siap di-upload! PageSpeed dijamin hijau!
Gila kan, Bro? Cuma butuh sekian milidetik buat ngeproses resize gambar resolusi raksasa pakai algoritma Lanczos3. Dari sini, script ini bisa lu kembangin lagi. Lu bisa masukin looping buat nge-batch ratusan gambar sekaligus.
Eksplorasi terus, Bro. Sampai jumpa di oprekan tech tingkat dewa selanjutnya!