3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Rustの関連型とジェネリクス

Last updated at Posted at 2021-12-01

この記事はEAGLYS Advent Calendar 2021の2日目の記事です。

モチベーション

タイトルの2つの違いがどうにも分かりづらかったので、自分なりに解説します。

https://doc.rust-jp.rs/rust-by-example-ja/generics/assoc_items.html

簡単なまとめ

実装できる回数 型パラメタ
ジェネリクス 1回以上 必要
関連型 1回 不要

関連型

まず、 IntPackPack トレイトを実装して標準出力します。関連型を使った場合は以下です。

use std::fmt::Debug;
#[derive(Debug)]
struct IntPack(i32);
trait Pack {
	type A;
	fn get(&self)->Self::A;
	fn set(&mut self,content:Self::A)->();
}

impl Pack for IntPack {
	type A = i32;
	fn get(&self) -> Self::A {
		self.0
	}
	fn set(&mut self, content: Self::A)->(){
		self.0 = content;
	}
}

fn debug<A:Pack+Debug>(a:A){
	dbg!(a);
}

fn main() {
	let int_pack = IntPack(10);
    debug(int_pack);
}

ジェネリクスを使った場合はIntPackのi32 にしか実装しないことがわかっているのに、型パラメタを指定しなければなりません。ジェネリクスの場合、複数のPack<A>の実装がコードに存在しうるからです。

use std::fmt::Debug;
#[derive(Debug)]
struct IntPack(i32);
trait Pack<A> {	
	fn get(&self)->A;
	fn set(&mut self,content:A)->();
}

impl Pack<i32> for IntPack {
	fn get(&self) -> i32 {
		self.0
	}
	fn set(&mut self, content: i32)->(){
		self.0 = content;
	}
}

fn debug<A:Pack<i32>+Debug>(a:A){
	dbg!(a);
}

fn main() {
	let int_pack = IntPack(10);
    debug(int_pack);
}

AについてだけPackトレイトが実装されることがわかっている場合、関連型は有用です。

ジェネリクスの強みは?

関連型の場合、一回しかPackトレイトを実装できません。これは関連型というよりは、トレイトの仕様です。複数実装すると impl Pack for IntPack {~} が衝突します。

use std::fmt::Debug;
#[derive(Debug)]
struct IntPack(i32);
trait Pack {
	type A;
	fn get(&self)->Self::A;
	fn set(&mut self,content:Self::A)->();
}

impl Pack for IntPack {
	type A = i32;
	fn get(&self) -> Self::A {
		self.0
	}
	fn set(&mut self, content: Self::A)->(){
		self.0 = content;
	}
}

// conflicting implementations of trait `Pack` for type `IntPack`
impl Pack for IntPack {
	type A = i64;
	fn get(&self) -> Self::A {
		self.0
	}
	fn set(&mut self, content: Self::A)->(){
		self.0 = content;
	}
}

fn debug<A:Pack+Debug>(a:A){
	dbg!(a);
}

fn main() {
	let int_pack = IntPack(10);
    debug(int_pack);
}

ジェネリクスの場合、複数回実装が可能です。impl Pack<A> for IntPack {~} のAが異なれば、それは異なる実装になるので、実装が衝突することはありません。

use std::fmt::Debug;
#[derive(Debug)]
struct IntPack(i32);
trait Pack<A> {	
	fn get(&self)->A;
	fn set(&mut self,content:A)->();
}

impl Pack<i32> for IntPack {
	fn get(&self) -> i32 {
		self.0
	}
	fn set(&mut self, content: i32)->(){
		self.0 = content;
	}
}

impl Pack<i64> for IntPack {
	fn get(&self) -> i64 {
		self.0 as i64
	}
	fn set(&mut self, content: i64)->(){
		self.0 = content as i32;
	} 
}

fn debug<A:Pack<i32>+Debug>(a:A){
	dbg!(a);
}

fn debug64<A:Pack<i64>+Debug>(a:A){
	dbg!(a);
}

fn main() {
	let int_pack = IntPack(10);
    debug(int_pack);
}

まとめ

関連型とジェネリクスの使い分けは

  • 複数回実装可能だが、型パラメータを指定しないといけないのがジェネリクス
  • 一回しか実装できないが、型パラメータを指定しなくてよいのが関連型

と言えます。

3
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?