Slice
A modern IDL and serialization format
IDL for RPCs
Slice is a new Interface Definition Language (IDL) that you use to model and describe your network API in a clear and concise manner.
Defining the customary Greeter service interface in Slice is straightforward:
interface Greeter { greet(name: string) -> string}You don't need to craft special request and reply message types for the greet operation: you can specify all your parameters inline.
Slice and IceRPC
You can use Slice with any RPC framework, or with no RPC framework at all. Slice is a separate framework that does not depend on IceRPC.
That being said, Slice and IceRPC were developed in tandem and together form what you would typically understand as an "RPC framework".
This chapter describes both Slice and the IceRPC + Slice integration provided by IceRPC.
Streamlined syntax
Slice provides a readable, modern syntax inspired by Rust and Swift. It includes support for streaming parameters with the stream keyword, allows you to turn any type into an optional type with the ? suffix, requires minimal punctuation, and more.
The Slice primitive types have clear names such as uint8, float64, and string. Slice also includes two built-in generic types, Sequence and Dictionary. And Slice lets you define your own types with custom, enum, and struct.
Last but not least, Slice's tagged fields and parameters allow you to update your structs and operations over time without breaking on-the-wire compatibility.
Example
// A TourGuide interface with an operation that describes an attraction defined with Slice.interface TourGuide { audioDescription(attraction: string) -> ( title: string duration: WellKnownTypes::Duration recording: stream uint8 )}Code generation
Once you've described your RPCs in Slice, you run the Slice compiler on your Slice definitions to generate code in the programming language of your choice. This generated code makes it easy to send requests to a remote service and later receive the corresponding responses. It also helps you implement this remote service by providing a template to fill-in.
For example, the C# code generator generates two C# interfaces and a record struct from the interface Greeter shown earlier:
internal partial interface IGreeter{ Task<string> GreetAsync( string name, IFeatureCollection? features = null, CancellationToken cancellationToken = default);}
internal readonly partial record struct GreeterProxy : IGreeter, IProxy{ public Task<string> GreetAsync( string name, IFeatureCollection? features = null, CancellationToken cancellationToken = default) { // implemented using IceRPC... }}internal partial interface IGreeterService{ ValueTask<string> GreetAsync( string name, IFeatureCollection features, CancellationToken cancellationToken);}The C# code shown above is generated by the IceRPC-specific code generator for C#. In particular, IFeatureCollection is an IceRPC type.
Flexible and extensible
You can fine-tune the API generated by the Slice code generators with Slice attributes. For example, in C#, you can map a sequence to an array (the default), or to a list, or even to a hash set:
interface Translator { getLanguages() -> [cs::type("HashSet<string>")] Sequence<string>}The cs::type attribute changes the mapping for values decoded by the generated code and returned to you. For values you give to the generated code, the mapping for a Sequence<string> parameter is always IEnumerable<string>.
Slice's custom types allow you to send any type you wish through Slice. You just need to provide methods to encode and decode instances of your custom type.