Ta upp ett Rust-projekt och du hittar en Cargo.toml. Klona ett GitHub-repo och
du hittar en .github/workflows/-mapp full av YAML. Båda formaten gör samma jobb på ytan —
lagrar strukturerad konfiguration som människor redigerar — men de gör väldigt olika kompromisser.
Om du någonsin haft YAML att tyst förvränga ett värde för dig, eller undrat varför Rust valde TOML
istället för YAML för sitt paketmanifest, är den här artikeln för dig.
Kärnsskillnaden: explicit vs implicit
Den grundläggande uppdelningen mellan TOML och YAML handlar om hur mycket parsern tillåts
gissa. TOML är explicit: varje
värde har en otvetydig typ. Strängar citeras alltid. Booleaner är exakt true
eller false. Datum/tider är förstklassiga värden med sin egen syntax. Det finns ingen
implicit typkoercering — parsern försöker inte vara smart.
YAML lutar åt andra hållet.
Det försöker vara bekvämt: du behöver inga citationstecken runt de flesta strängar, och parsern
härledar typer från värdets utseende. Det är det härledandet som biter folk. YAML 1.1-specifikationen
(fortfarande använd av många verktyg) behandlar yes, no, on, off,
true och false alla som booleaner. Den behandlar okoterade versionssträngar som
1.0 som flyttal. Och sedan är det Norge-problemet.
YAMLs berömda fallgropar
Norge-problemet blev något av ett meme i DevOps-kretsar. I YAML 1.1 parsas landskoden
NO som booleanen false. Så en konfiguration som mappar ISO-landskoder
till inställningar skulle tyst konvertera Norges post till ett booleskt värde.
YAML 1.2-specifikationen fixade detta,
men många flitigt använda parsers — inklusive PyYAMLs standardläge tills nyligen — riktar sig fortfarande mot YAML 1.1.
Kontrollera vilken spec-version dina verktyg faktiskt implementerar innan du litar på dem.
NO,
Yes, on och off alla booleaner. Lösningen är enkel —
citera dina strängar — men problemet är att det är tyst. Din konfiguration laddas utan fel och du
får ett booleskt värde där du förväntade dig en sträng.# YAML 1.1 implicit type coercion — all of these silently become booleans:
countries:
norway: NO # → false ← The Norway Problem
sweden: SE # → "SE" (fine, not in the boolean list)
enabled: yes # → true
disabled: no # → false
feature_flag: on # → true
another: off # → false
# Version strings can become numbers:
python_version: 3.10 # → float 3.1 (trailing zero dropped)
api_version: 1.0 # → float 1.0
# Safe: quote anything that could be ambiguous
python_version: "3.10"
country: "NO"
enabled: "yes"- Oktala literaler. I YAML 1.1 parsas
010som8(oktalt), inte10. Det spelar roll för filbehörighetsvärden som0755. - Tab vs mellanslag. YAML förbjuder tabbtecken för indragning. Klistra in kod från en editor konfigurerad för att använda tabbar och du får ett kryptiskt parse-fel — eller värre, tyst feljustering.
- Implicita null.. En nyckel utan värde blir
null. Lätt att skapa av misstag vid manuell redigering. - Indragningskänslighet. Ett extra mellanslag och ett värde skiftar tyst från en förälder till en annan. Inget fel, bara fel data.
TOML: hur explicita typer faktiskt ser ut
TOMLs typsystem beskrivs i
TOML v1.0-specifikationen. Strängar måste
citeras (enkla eller dubbla). Booleaner är true eller false och inget annat.
Heltal är heltal. Flyttal är flyttal. Och datum/tider — något som varken JSON eller YAML har som
en inbyggd typ — är förstklassiga värden i TOML.
# TOML types are always unambiguous
name = "my-app" # string — must be quoted
version = "1.0.0" # string — quotes make it clear this is not a float
port = 8080 # integer
debug = false # boolean — only true/false, nothing else
threshold = 0.95 # float
# Datetime is a first-class type — no string parsing needed
created_at = 2024-01-15T09:30:00Z
build_date = 2024-03-20
# Arrays
allowed_hosts = ["localhost", "staging.example.com", "api.example.com"]
# Inline tables
[database]
host = "postgres.internal"
port = 5432
name = "payments_prod"
pool_size = 20Landskoden NO är bara en sträng i TOML eftersom den inte är citerad, men TOML
härleder inte typer från strängliknande värden — okoterade värden följer strikta syntaktiska regler.
För att vara en sträng behöver den citationstecken. TOML har helt enkelt inte den implicita koerceringsmekanismen
som orsakar YAMLs fallgropar.
Sida vid sida: en CI-pipeline-konfiguration
Här är ett GitHub Actions-arbetsflöde — det här är YAMLs hemmaplan. Formatet passar naturligt eftersom GitHub Actions förväntar sig YAML, indragningsbaserad nesting speglar den logiska strukturen och kommentarer är väsentliga för att förklara icke-uppenbara stegkonfigurationer.
# .github/workflows/release.yml
name: Release
on:
push:
tags:
- 'v*'
jobs:
build-and-publish:
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
steps:
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build
run: npm run build --if-present
- name: Publish to npm
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}Här är nu det ekvivalenta projektmanifestet i TOML — det är där TOML lyser. Jämför en riktig Cargo.toml-struktur med hur samma konfiguration skulle se ut i YAML:
# Cargo.toml — Rust package manifest
[package]
name = "payments-service"
version = "2.4.1"
edition = "2021"
description = "Payment processing microservice"
license = "MIT"
authors = ["Alice Chen <[email protected]>"]
[dependencies]
tokio = { version = "1.35", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
sqlx = { version = "0.7", features = ["postgres", "runtime-tokio-native-tls"] }
tracing = "0.1"
anyhow = "1.0"
[dev-dependencies]
tokio-test = "0.4"
wiremock = "0.6"
[profile.release]
opt-level = 3
lto = true
codegen-units = 1TOMLs svagheter: Array of Tables
TOML är inte utan sina ojämna kanter. Syntaxen för arraytabeller — [[dubbla parenteser]]
— är ett av de mer förvirrande sakerna i specifikationen. Det är hur du uttrycker vad JSON skulle skriva som
en array av objekt, och det läses konstigt till en början.
# TOML array of tables — [[double brackets]] creates array entries
[[server]]
host = "web-01.example.com"
port = 443
region = "us-east-1"
[[server]]
host = "web-02.example.com"
port = 443
region = "us-west-2"
[[server]]
host = "web-03.example.com"
port = 443
region = "eu-west-1"
# The above is equivalent to this JSON:
# { "server": [
# { "host": "web-01.example.com", "port": 443, "region": "us-east-1" },
# { "host": "web-02.example.com", "port": 443, "region": "us-west-2" },
# { "host": "web-03.example.com", "port": 443, "region": "eu-west-1" }
# ]}I YAML läser samma struktur mer naturligt — bara en lista med indragna egenskaper.
För djupt kapslad data med upprepade arrayposter är YAMLs indragningsbaserade nesting genuint
mindre utförlig än TOMLs [[sektion]]-rubriker. TOML har heller ingen ekvivalent till YAMLs
ankare-och-alias-system, så du kan inte definiera ett delat block en gång och referera till det på annat håll.
Om du befinner dig med att duplicera samma uppsättning värden i flera TOML-tabeller är du fast med att
kopiera dem manuellt.
Var varje format vinner i praktiken
YAML vinner som standard i dessa ekosystem:
- GitHub Actions. Hela arbetsflödessyntaxen är YAML. Du har inget val här, och det är okej — indragningen mappar väl till den kapslade strukturen av jobb, steg och villkor.
- Kubernetes. Varje manifest — Deployments, Services, ConfigMaps, Ingress-regler — är YAML. Kubernetes objektmodell är djupt kapslad och YAML hanterar det elegant.
- Docker Compose. Tjänstedefinitioner, nätverk, volymer — allt YAML. Kommentarer som förklarar varför en port är exponerad eller varför ett specifikt healthcheck-intervall används är en del av dokumentationen.
- Ansible. Playbooks, roller, variabelfiler — YAML genomgående. Kommentarsstödet används aktivt för att förklara icke-uppenbara uppgiftsparametrar.
TOML vinner när du kontrollerar formatet:
- Rust-projekt.
Cargo.tomlär guldstandarden. Beroendedeklarationer, funktionsflaggor, byggprofiler — allt i TOML. De explicita typerna innebär att versionssträngar som"1.0.0"förblir strängar. - Python-projekt.
pyproject.tomlhar blivit standarden för Python-projektmetadata, byggkonfiguration och verktygsinställningar (Black, isort, mypy, pytest läser alla från den). - Verktygskonfiguration där tvetydighet orsakar buggar. Om din konfiguration innehåller versionssträngar, landskoder eller andra värden som ser ut att kunna vara booleaner eller tal eliminerar TOMLs explicita typer en hel kategori av parsöverraskningar.
- Platta konfigurationer. När din konfiguration till stor del är nyckel-värde-par på en eller två nivåer av nesting är TOML mer läsbar än YAML och renare än JSON.
Den praktiska beslutsguiden
Det mesta av tiden görs valet åt dig av ekosystemet. GitHub Actions är YAML. Kubernetes är YAML. Rust är TOML. Python-verktyg är TOML. När du faktiskt har ett fritt val, använd detta som din guide:
- Använd YAML när verktyget kräver det — CI/CD-plattformar, Kubernetes, Helm charts, Docker Compose, Ansible. Att kämpa mot konventionen kostar mer än det sparar.
- Använd YAML när du behöver ankare och alias för att hålla en komplex konfiguration DRY — det finns ingen TOML-ekvivalent.
- Använd TOML för projektmanifest och verktygskonfiguration du kontrollerar — paketmetadata, linter-inställningar, byggkonfiguration.
- Använd TOML när din konfiguration innehåller värden som YAML 1.1 kan feltolka — versionssträngar, landskoder, vad som helst som liknar ett booleskt värde eller tal.
- Citera dina YAML-strängar när värdet kan förväxlas med ett booleskt värde,
tal eller null. Speciellt: versionsnummer, landskoder, vad som helst som börjar med en siffra
och värden som
yes,no,on,off.
Arbeta med båda formaten
Behöver du validera eller formatera en konfigurationsfil du arbetar med? TOML Formatter hanterar TOML-filer och YAML Formatter täcker YAML. Om du behöver migrera en konfiguration från ett format till ett annat — säg, konvertera en YAML-baserad verktygskonfiguration till TOML för ett projekt som föredrar det — konverterar båda TOML till JSON och YAML till JSON till JSON, som du sedan kan konvertera till ditt målformat. Ibland är att gå igenom JSON som ett mellanliggande steg den mest pålitliga vägen.
Värt att veta: båda formaten har sina egna JSON-kompatibla delmängder. Giltig JSON är giltig YAML (YAML är en supermängd av JSON). TOML har inte den relationen med JSON, men TOML-specifikationen hålls avsiktligt enkel — det är en kort läsning, vilket är mer än du kan säga om hela YAML 1.2-specifikationen.
Sammanfattning
TOML och YAML konkurrerar inte riktigt. De har slagit sig ned i olika nischer baserat på sina designprioriteringar. YAMLs implicita typer och indragningsbaserade struktur gör det till den naturliga passformen för stora, kapslade konfigurationer underhållna av team — tänk Kubernetes-manifest och GitHub Actions-arbetsflöden. TOMLs explicita typer och platta sektionsstruktur gör det till den naturliga passformen för projektmanifest och verktygskonfiguration där en feltolkad versionssträng eller landskod skulle orsaka en riktig bugg.
Det enda att ta med sig: om du skriver YAML och har några värden som
ser ut att kunna vara booleaner, tal eller null — citera dem. Det är den enda vanan som
förhindrar de flesta YAML-relaterade konfigurationsbuggar. Och om du startar ett nytt projekt och
fritt kan välja ditt konfigurationsformat är TOML värt en titt.
TOML GitHub-repositoriet
har bra exempel, och när du väl har skrivit en Cargo.toml eller pyproject.toml
blir formatets lockelse ganska tydlig.