Genéricos

Los genéricos en C# proporcionan una forma de crear definiciones para tipos y métodos que pueden ser parametrizados sobre otros tipos. Esto mejora la reutilización de código, la seguridad de tipos y el rendimiento (por ejemplo, evita conversiones en tiempo de ejecución). Considera el siguiente ejemplo de un tipo genérico que agrega una marca de tiempo a cualquier valor:

using System;

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

Rust también tiene genéricos, como se muestra en el equivalente del ejemplo anterior:

use std::time::*;

struct Timestamped<T> { value: T, timestamp: SystemTime }

impl<T> Timestamped<T> {
    fn new(value: T) -> Self {
        Self { value, timestamp: SystemTime::now() }
    }
}

Mira también:

Restricciones de tipo genérico

En C#, los tipos genéricos pueden ser restringidos usando la palabra clave where. El siguiente ejemplo muestra tales restricciones en C#:

using System;

// Nota: los registros implementan automáticamente `IEquatable`. La siguiente
// implementación muestra esto explícitamente para una comparación con 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);
}

Lo mismo se puede lograr en 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
    }
}

En Rust, las restricciones de tipo genérico se llaman bounds.

En la versión de C#, las instancias de Timestamped<T> solo pueden crearse para T que implementen IEquatable<T> ellos mismos, pero ten en cuenta que la versión de Rust es más flexible porque Timestamped<T> implementa condicionalmente PartialEq. Esto significa que las instancias de Timestamped<T> aún pueden crearse para algunos T que no son equiparables, pero entonces Timestamped<T> no implementará la igualdad a través de PartialEq para dicho T.

Mira también: