Generics are a way of writing code that is independent of the specific types being used. Functions and types may now be written to use any of a set of types.
Generics adds three new big things to the language:
- Type parameters for function and types.
- Defining interface types as sets of types, including types that don’t have methods.
- Type inference, which permits omitting type arguments in many cases when calling a function.
package main
import ("fmt")
type Number interface {
int8 | int64 | float64
}
func sumNumbers[N Number](s []N) N {
var total N
for _, num := range s {
total += num
}
return total
}
func main() {
ints := []int64{32, 64, 96, 128}
floats := []float64{32.0, 64.0, 96.1, 128.2}
bytes := []int8{8, 16, 24, 32}
fmt.Println(sumNumbers(ints))
fmt.Println(sumNumbers(floats))
fmt.Println(sumNumbers(bytes))
}
type Number interface {
int8 | int64 | float64
}
type CustomSlice[T Number] []T
func Print[N Number, T CustomSlice[N]] (s T) {
for _, v := range s {
fmt.Println(v)
}
}
func main(){
sl := CustomSlice[int64]{32, 32, 32}
Print(sl)
}
Rust
Rust accomplishes generic by performing monomorphization of the code that is using generics at compile time. Monomorphization is the process of turning generic code into specific code by filling in the concrete types that are used when compiled.
x: T,
y: T,
}
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}
fn main() {
let p = Point { x: 5, y: 10 };
println!("p.x = {}", p.x());
}
// Expressing bounds with a `where` clause
impl <A, D> MyTrait<A, D> for YourType where
A: TraitB + TraitC,
D: TraitE + TraitF {}
struct Years(i64);
fn main() {
let years = Years(42);
let years_as_primitive_1: i64 = years.0; // Tuple
let Years(years_as_primitive_2) = years; // Destructuring
}
Associated types
The use of "Associated types" improves the overall readability of code by moving inner types locally into a trait as output types. Syntax for the trait definition is as follows:
// (Note: `type` in this context is different from `type` when used for
// aliases).
trait Contains {
type A;
type B;
// Updated syntax to refer to these new types generically.
fn contains(&self, _: &Self::A, _: &Self::B) -> bool;
}
// Without using associated types
fn difference<A, B, C>(container: &C) -> i32 where
C: Contains<A, B> { ... }
// Using associated types
fn difference<C: Contains>(container: &C) -> i32 { ... }
use std::marker::PhantomData;
// A phantom tuple struct which is generic over `A` with hidden parameter `B`.
#[derive(PartialEq)] // Allow equality test for this type.
struct PhantomTuple<A, B>(A,PhantomData<B>);
// A phantom type struct which is generic over `A` with hidden parameter `B`.
#[derive(PartialEq)] // Allow equality test for this type.
struct PhantomStruct<A, B> { first: A, phantom: PhantomData<B> }
// Note: Storage is allocated for generic type `A`, but not for `B`.
// Therefore, `B` cannot be used in computations.
fn main() {
// Here, `f32` and `f64` are the hidden parameters.
// PhantomTuple type specified as `<char, f32>`.
let _tuple1: PhantomTuple<char, f32> = PhantomTuple('Q', PhantomData);
// PhantomTuple type specified as `<char, f64>`.
let _tuple2: PhantomTuple<char, f64> = PhantomTuple('Q', PhantomData);
// Type specified as `<char, f32>`.
let _struct1: PhantomStruct<char, f32> = PhantomStruct {
first: 'Q',
phantom: PhantomData,
};
// Type specified as `<char, f64>`.
let _struct2: PhantomStruct<char, f64> = PhantomStruct {
first: 'Q',
phantom: PhantomData,
};
}
No comments:
Post a Comment