Section 10/111 menit
10. Real Use Cases
10. Real Use Cases
Use Case 1: Image Processing Pipeline
swift
import Foundation
import CoreImage
// Large image processing dengan ownership yang jelas
struct ProcessableImage {
var cgImage: CGImage
var metadata: [String: Any]
var sizeInBytes: Int {
cgImage.width * cgImage.height * cgImage.bitsPerPixel / 8
}
}
struct ImagePipeline {
// Hanya inspect — borrowing
static func analyze(image: borrowing ProcessableImage) -> ImageAnalysis {
let brightness = computeBrightness(image.cgImage)
let sharpness = computeSharpness(image.cgImage)
return ImageAnalysis(brightness: brightness, sharpness: sharpness, size: image.sizeInBytes)
}
// Transform — consuming + hasilkan baru
static func resize(
_ image: consuming ProcessableImage,
to size: CGSize
) -> ProcessableImage {
let resizedCG = resizeCGImage(image.cgImage, to: size)
return ProcessableImage(
cgImage: resizedCG,
metadata: image.metadata // metadata di-copy (kecil, OK)
)
}
static func applyFilter(
_ image: consuming ProcessableImage,
filter: CIFilter
) -> ProcessableImage {
let ciImage = CIImage(cgImage: image.cgImage)
filter.setValue(ciImage, forKey: kCIInputImageKey)
let outputCI = filter.outputImage!
let outputCG = CIContext().createCGImage(outputCI, from: outputCI.extent)!
return ProcessableImage(cgImage: outputCG, metadata: image.metadata)
}
// Consuming final step
static func export(
_ image: consuming ProcessableImage,
format: ExportFormat,
quality: Double
) -> Data {
switch format {
case .jpeg: return encodeJPEG(image.cgImage, quality: quality)
case .png: return encodePNG(image.cgImage)
case .webp: return encodeWebP(image.cgImage, quality: quality)
}
}
}
// Penggunaan:
func processUserAvatar(original: ProcessableImage) async -> Data {
// Analyze dulu (borrowing — tidak berubah)
let analysis = ImagePipeline.analyze(image: original)
// Proses (consuming pipeline)
let processed = ImagePipeline
.resize(original, to: CGSize(width: 256, height: 256)) // original di-consume
// original tidak bisa dipakai lagi setelah ini ↑
let filtered: ProcessableImage
if analysis.sharpness < 0.5 {
filtered = ImagePipeline.applyFilter(processed, filter: sharpenFilter)
} else {
filtered = processed // move — tidak ada copy
}
return ImagePipeline.export(filtered, format: .webp, quality: 0.85)
}
struct ImageAnalysis { var brightness, sharpness: Double; var size: Int }
enum ExportFormat { case jpeg, png, webp }
func computeBrightness(_ image: CGImage) -> Double { 0.5 }
func computeSharpness(_ image: CGImage) -> Double { 0.7 }
func resizeCGImage(_ image: CGImage, to size: CGSize) -> CGImage { image }
func encodeJPEG(_ image: CGImage, quality: Double) -> Data { Data() }
func encodePNG(_ image: CGImage) -> Data { Data() }
func encodeWebP(_ image: CGImage, quality: Double) -> Data { Data() }
let sharpenFilter = CIFilter()
Use Case 2: Query Builder dengan consuming
swift
// SQL/NoSQL query builder yang type-safe dan zero-copy
struct QueryBuilder {
private var table: String
private var conditions: [String] = []
private var orderByClause: String?
private var limitCount: Int?
private var selectColumns: [String] = ["*"]
init(table: String) {
self.table = table
}
// consuming: setiap step menghasilkan builder baru
consuming func select(_ columns: String...) -> QueryBuilder {
var builder = self
builder.selectColumns = columns
return builder
}
consuming func `where`(_ condition: String) -> QueryBuilder {
var builder = self
builder.conditions.append(condition)
return builder
}
consuming func orderBy(_ column: String, ascending: Bool = true) -> QueryBuilder {
var builder = self
builder.orderByClause = "\(column) \(ascending ? "ASC" : "DESC")"
return builder
}
consuming func limit(_ count: Int) -> QueryBuilder {
var builder = self
builder.limitCount = count
return builder
}
// Terminal operation — consuming builder, menghasilkan SQL string
consuming func toSQL() -> String {
var sql = "SELECT \(selectColumns.joined(separator: ", ")) FROM \(table)"
if !conditions.isEmpty {
sql += " WHERE " + conditions.joined(separator: " AND ")
}
if let order = orderByClause {
sql += " ORDER BY \(order)"
}
if let limit = limitCount {
sql += " LIMIT \(limit)"
}
return sql
}
}
// Penggunaan — ergonomis dan type-safe
let query = QueryBuilder(table: "users")
.select("id", "name", "email")
.where("active = true")
.where("age >= 18")
.orderBy("created_at", ascending: false)
.limit(10)
.toSQL()
// query = "SELECT id, name, email FROM users WHERE active = true AND age >= 18 ORDER BY created_at DESC LIMIT 10"
Use Case 3: Data Serializer dengan borrowing
swift
// High-performance serializer — borrowing untuk semua input
struct BinarySerializer {
private var buffer: [UInt8] = []
// Semua encode methods: borrowing — tidak perlu copy input
mutating func encode(string: borrowing String) {
let bytes = Array(string.utf8)
encode(uint32: UInt32(bytes.count))
buffer.append(contentsOf: bytes)
}
mutating func encode(uint32 value: borrowing UInt32) {
withUnsafeBytes(of: value.bigEndian) { buffer.append(contentsOf: $0) }
}
mutating func encode(data: borrowing Data) {
encode(uint32: UInt32(data.count))
buffer.append(contentsOf: data)
}
mutating func encode(array: borrowing [String]) {
encode(uint32: UInt32(array.count))
for item in array {
encode(string: item) // borrow setiap item
}
}
// consuming: hasilkan final Data
consuming func finalize() -> Data {
Data(buffer)
}
}
struct UserMessage {
var id: String
var content: String
var attachments: [String]
var timestamp: UInt32
}
// Serialize tanpa copy UserMessage
func serialize(message: borrowing UserMessage) -> Data {
var serializer = BinarySerializer()
serializer.encode(string: message.id)
serializer.encode(string: message.content)
serializer.encode(array: message.attachments)
serializer.encode(uint32: message.timestamp)
return serializer.finalize()
}
// Penggunaan — message tidak di-copy
let message = UserMessage(id: "msg_123", content: "Hello!", attachments: [], timestamp: 1234567890)
let serialized = serialize(message: message) // borrowing — tidak ada copy
// message masih valid di sini
print(message.id) // ✅ OK