Migrating from Kotlin to Gossamer¶
Kotlin and Gossamer share null-safe habits, expression-oriented
control flow, lambdas, and pattern-style branching. Gossamer is not
JVM-hosted and does not use exceptions or suspend; it uses explicit
Option<T>, Result<T, E>, goroutines, and channels.
Quick Map¶
| Kotlin | Gossamer |
|---|---|
val x = 5 |
let x = 5 |
var x = 5 |
let mut x = 5 |
fun f(x: Int): Int = x + 1 |
fn f(x: i64) -> i64 { x + 1 } |
{ x: Int -> x + 1 } |
|x: i64| x + 1 |
if (c) a else b |
if c { a } else { b } |
when (x) { ... } |
match x { ... } |
data class User(...) |
struct User { ... } |
User("Ada", 36) |
User { name: "Ada", age: 36 } |
sealed class |
enum |
T? |
Option<T> |
try / catch |
Result<T, E> |
launch { ... } |
go fn() { ... }() |
async { ... }.await() |
channel send and receive |
println("$name") |
println!("{name}") |
Null Safety To Option¶
Kotlin:
Gossamer:
let len = name.map(|s: String| s.len())
let display = name.unwrap_or("anonymous")
let forced = strings::to_uppercase(&name.unwrap())
if let Some(n) = name {
println!("hello, {n}")
}
None only exists inside Option<T>. It is not a universal null.
Data Classes To Structs¶
data class User(val name: String, val age: Int)
val u = User("Ada", 36)
val older = u.copy(age = 37)
struct User {
name: String,
age: i64,
}
let u = User { name: "Ada", age: 36 }
let older = User { age: 37, ..u }
Named structs use braces. Tuple-like parentheses are for enum tuple variants and tuple structs, not ordinary named structs.
Sealed Classes To Enums¶
sealed class Outcome
data class Win(val message: String) : Outcome()
data class Loss(val message: String) : Outcome()
object Draw : Outcome()
enum Outcome {
Win(String),
Loss(String),
Draw,
}
match outcome {
Outcome::Win(message) => println!("{message}"),
Outcome::Loss(message) => eprintln!("{message}"),
Outcome::Draw => println!("draw"),
}
match is exhaustive, so adding a new enum variant forces call sites to
handle it.
Exceptions To Result¶
Kotlin:
Gossamer:
use std::{errors, fs}
fn read_config(path: &String) -> Result<Config, errors::Error> {
let text = fs::read_to_string(path)?
parse_config(&text)
}
Use match when a caller should recover locally:
let cfg = match read_config(&path) {
Ok(v) => v,
Err(e) => {
eprintln!("config error: {e}")
default_config()
},
}
Collections¶
Kotlin collection chains become std::iter pipelines. The pipe operator
passes the value on the left into the last argument of the next call.
use std::iter
let total = [1, 2, 3, 4]
|> iter::filter(|n: i64| n % 2 == 0)
|> iter::sum_by(|n: i64| n * n)
Mutating operations stay method-shaped:
Coroutines To Goroutines¶
let (tx, rx) = channel()
go fn() {
tx.send(fetch_data(&url))
tx.close()
}()
if let Some(result) = rx.recv() {
println!("{result}")
}
For fan-out and fan-in, use sync::WaitGroup:
let wg = sync::WaitGroup::new()
let (tx, rx) = channel()
for url in urls {
wg.add(1)
let tx = tx.clone()
go fn() {
defer wg.done()
tx.send(http::get(&url, []))
}()
}
go fn() {
wg.wait()
tx.close()
}()
while let Some(result) = rx.recv() {
handle(result)
}
Associated Functions¶
Kotlin companion factories become associated functions:
struct Connection { host: String }
impl Connection {
pub fn local() -> Connection {
Connection { host: "localhost" }
}
}
const DEFAULT_PORT: i64 = 5432
Standard Library Map¶
| Kotlin / JVM | Gossamer |
|---|---|
File(path).readText() |
fs::read_to_string(path) |
File(path).readBytes() |
fs::read(path) |
File(path).writeText(s) |
fs::write(path, s) |
System.getenv("X") |
env::var("X") |
ProcessBuilder(cmd).start() |
process::run(cmd, &args) |
System.exit(0) |
process::exit(0) |
println(x) |
println!("{x}") |
Regex(pattern) |
regex::compile(pattern) |
s.trim() |
strings::trim(&s) |
s.uppercase() |
strings::to_uppercase(&s) |
s.toInt() |
strconv::parse_i64(&s) |
listOf(...) |
[...] |
mutableListOf(...) |
let mut xs = [...] |
mapOf(k to v) |
HashMap::new() plus insert |
setOf(...) |
HashSet::new() plus insert |
OkHttp / Ktor HttpClient |
http::Client::new() or http::get(url, []) |
ktor server { ... } |
http::serve(addr, handler) |
kotlinx.serialization |
encoding::json |
kotlinx.coroutines.launch |
go fn() { ... }() |
Mutex() |
sync::Mutex::new() |
CountDownLatch(n) |
sync::WaitGroup::new() |