Generics

Generics in C# provide a way to create definitions for types and methods that can be parameterized over other types. This improves code reuse, type-safety and performance (e.g. avoid run-time casts). Consider the following example of a generic type that adds a timestamp to any value:

using System; sealed record Timestamped<T>(DateTime Timestamp, T Value) { public Timestamped(T value) : this(DateTime.UtcNow, value) { } }

Rust also has generics as shown by the equivalent of the above:

use std::time::*; struct Timestamped<T> { value: T, timestamp: SystemTime } impl<T> Timestamped<T> { fn new(value: T) -> Self { Self { value, timestamp: SystemTime::now() } } }

See also:

Generic type constraints

In C#, generic types can be constrained using the where clause. The following example shows such constraints in C#:

using System; // Note: records automatically implement `IEquatable`. The following // implementation shows this explicitly for a comparison to Rust. sealed record Timestamped<T>(DateTime Timestamp, T Value) : IEquatable<Timestamped<T>> where T : IEquatable<T> { public Timestamped(T value) : this(DateTime.UtcNow, value) { } public bool Equals(Timestamped<T>? other) => other is { } someOther && Timestamp == someOther.Timestamp && Value.Equals(someOther.Value); public override int GetHashCode() => HashCode.Combine(Timestamp, Value); }

The same can be achieved in Rust:

use std::time::*; struct Timestamped<T> { value: T, timestamp: SystemTime } impl<T> Timestamped<T> { fn new(value: T) -> Self { Self { value, timestamp: SystemTime::now() } } } impl<T> PartialEq for Timestamped<T> where T: PartialEq { fn eq(&self, other: &Self) -> bool { self.value == other.value && self.timestamp == other.timestamp } }

Generic type constraints are called bounds in Rust.

In C# version, Timestamped<T> instances can only be created for T which implement IEquatable<T> themselves, but note that the Rust version is more flexible because it Timestamped<T> conditionally implements PartialEq. This means that Timestamped<T> instances can still be created for some non-equatable T, but then Timestamped<T> will not implement equality via PartialEq for such a T.

See also: