If you've been following along, you've got the basics of structs and methods down. But sometimes, you don't care what an object is—you only care that it knows how to do something. That’s where interfaces—or as we call them in Skink, Templates—come in.
Templates: The "If it walks like a duck" approach
In many languages, you have to explicitly say "This thing is a Printer." In Skink, we lean into duck typing with template. If your struct has the methods defined in the template, it is a printer. No paperwork required.
template Printer {
fn Print(self: Printer, message: string)
}
struct Console {
pub fn Print(self: Console, message: string) {
print("{message}")
}
}
fn sayHello(p: Printer) {
p.Print("Hello from a template")
}
fn main() -> int {
c := Console{}
sayHello(c)
return 0
}
The compiler checks it at the call site. It’s clean, it’s fast, and it stops you from having to define a rigid hierarchy for every tiny task.
Services: The "For the record" approach
Sometimes, you need to be explicit. Maybe your project is getting so large that a named contract—a service—helps you keep your sanity, or perhaps you're building a tool that needs to inspect your interfaces.
service Writer {
fn Write(self: Writer, data: []byte) -> int
}
Think of service as the formal document you sign when you really need to be sure about the implementation. They're still a bit of a "work in progress" in the Skink ecosystem, so keep an eye on the release notes. For now, if you're just trying to get a simple status update printed, stick with template.
When to use which?
Use template when you want to write code that's flexible and doesn't care about the pedigree of the objects it’s working with. It keeps your code from getting bogged down in boilerplate.
Use service when you need a clear, named contract for your API surface or when you're working with tooling that needs to know exactly what a type provides.
Exercises
Don't spend too long overthinking the philosophy—try these out:
- The Name Game: Define a Named template with a Name(self: Named) -> string method and implement it for a Person struct.
- The Generic Greet: Write a function greet(n: Named) that prints "Hello, {n.Name()}!". See how it treats your Person object.
- Digging into the Docs: Take a look at std/rules.skink (if you have the standard library set up) and see if you can spot the RuleSource template. It’s a great way to see how the "pros" do it.
Next
When you're ready to actually touch the disk and make your data survive a power cycle, we'll talk about Reading and writing files.