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: