Swift — o herói inesperado da programação de sistemas
Quando se ouve "programação de sistemas", geralmente vêm à mente o rigoroso C, assembly ou, na pior das hipóteses, Rust. Swift? Aquela linguagem para aplicativos móveis bonitos? Acontece que sim. E não é brincadeira.
A Apple investiu no Swift não apenas conveniência e segurança, mas também controle total sobre memória, ponteiros e chamadas de sistema. Neste artigo, vamos explorar como o Swift pode se tornar sua principal ferramenta para escrever utilitários, drivers, daemons de rede e até componentes de sistema operacional. Prepare-se para se surpreender — o código será real, funcional e muito interessante.
Por que Swift? Três pilares da programação de sistemas
A programação de sistemas exige três coisas: desempenho, controle sobre recursos e a capacidade de trabalhar "próximo ao hardware". O Swift oferece tudo isso, e ainda mais.
- Desempenho no nível do C. O compilador LLVM otimiza o código tão bem quanto para C ou C++. Em benchmarks, Swift frequentemente supera Go e Rust em tarefas de I/O.
- Acesso direto à memória. Através dos mecanismos UnsafePointer, UnsafeMutablePointer e trabalho com bytes brutos.
- Biblioteca padrão rica. Não é necessário carregar um monte de dependências — tudo para trabalhar com sockets, arquivos, processos já está disponível.
Ao mesmo tempo, o Swift permanece seguro: se você não usar blocos unsafe, o compilador o protegerá contra estouro de buffer e ponteiros soltos. E quando precisar, basta escrever withUnsafePointer e obter controle total.
Trabalho com memória: de bytes a estruturas
Vamos começar com o básico — trabalho com memória bruta. Imagine que você precisa ler um arquivo binário e interpretá-lo como um array de estruturas. Em C, você sofreria com malloc e sizeof. Em Swift — elegante e seguro.
Leitura de arquivo binário em Swift
import Foundation
struct Header { let magic: UInt32 let version: UInt16 let count: UInt16}
func readBinaryFile(path: String) -> Header? { let data = try? Data(contentsOf: URL(fileURLWithPath: path)) guard let data = data else { return nil } return data.withUnsafeBytes { (ptr: UnsafeRawBufferPointer) in guard let baseAddress = ptr.baseAddress else { return nil } let headerPtr = baseAddress.assumingMemoryBound(to: Header.self) return headerPtr.pointee }}Observe: não alocamos memória manualmente. withUnsafeBytes fornece um ponteiro temporário que vive apenas dentro do closure. Isso é seguro, eficiente e lê-se como boa prosa.
Criação de estruturas e trabalho com ponteiros
E se precisar criar uma estrutura na memória e passar seu endereço para uma chamada de sistema? Fácil!
struct sockaddr_in { var sin_len: UInt8 = 0 var sin_family: UInt8 = UInt8(AF_INET) var sin_port: in_port_t = 0 var sin_addr: in_addr = in_addr(s_addr: 0) var sin_zero: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) = (0,0,0,0,0,0,0,0)}
var address = sockaddr_in()address.sin_port = CFSwapInt16HostToBig(8080)address.sin_addr.s_addr = inet_addr("127.0.0.1")
let ptr = withUnsafePointer(to: &address) { $0 }let rawPtr = UnsafeRawPointer(ptr)Este código é a base para trabalhar com sockets em baixo nível. Definimos manualmente a estrutura, preenchemos os campos e obtemos um ponteiro bruto para passar para connect ou bind.
Chamadas de sistema e API POSIX
O Swift se dá muito bem com POSIX. Você pode chamar open, read, write, fork, exec — tudo como nos bons e velhos tempos, mas com sintaxe do século XXI.
Criação de fork + exec em Swift
import Darwin
func spawnProcess(path: String, args: [Str