14
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AI時代に型システムがもっと大切になるって話 - Rust開発者の視点から学ぶ

Last updated at Posted at 2025-08-15

こんにちは!

最近AIコーディングツールを使いながら開発してるお盆暇暇甲子園見る系エンジニアです。

甲子園のクーリングタイムの間で、Rust言語の開発者として有名なNiko Matsakis氏の記事「Rust, Python, and TypeScript: the new trifecta」を読んで、めちゃくちゃ考えさせられたので、自分なりの解釈と感想をまとめてみます。

元記事の要点をサクッと紹介

Matsakis氏は「AIが普及すると、Rust、Python、TypeScriptの3言語が主流になる」という予測をしています。

  • Rust: 高パフォーマンスが必要な場面
  • Python: データサイエンス系のライブラリが豊富
  • TypeScript: Web開発の標準

そして「アイデア指向プログラミング」という概念を提唱。これは、開発者がアーキテクト的な役割を担い、AIに実装の詳細を任せる開発スタイルのことです。

型システム論がめっちゃ面白い

記事で一番刺さったのが型システムについての議論でした。

LLMは人間に似ている?

LLMは創造性があるけど、目の前にないものは忘れやすい

これ、めちゃくちゃ分かる!ClaudeやChatGPTとかと話してても、長い文脈だと前の方の内容を忘れちゃうことありますよね。

そこで型システムの出番です。

なぜ型システムが重要になるのか

従来: 型システムは主に開発者のためのもの(バグ防止、可読性向上)

AI時代: AIの「忘れやすさ」をカバーする重要な役割

例えば、Rustのenumを使えば

enum UserState {
    Active { last_login: DateTime },
    Inactive { suspended_since: DateTime },
    Banned { reason: String },
}

こうしておけば、AIも「このユーザーがActiveの時だけlast_loginにアクセスできる」ということを忘れにくくなります。

TypeScriptでも同じで

type User = 
  | { status: 'active'; lastLogin: Date }
  | { status: 'inactive'; suspendedSince: Date }
  | { status: 'banned'; reason: string };

型で状態と利用可能なデータを明示することで、AIが間違ったフィールドにアクセスするのを防げやすくなりそうです。

チーム開発での型システムの価値

個人的に、この「AI時代の型システム」論は、チーム開発にもそのまま当てはまると思っています。

人間も忘れやすい生き物

チームメンバーも、LLMと同じで

  • プロジェクトの全体像を常に把握しているわけじゃない
  • 他の人が書いたコードの細かい仕様は覚えていない
  • 数ヶ月前の自分のコードすら忘れる

型システムがドキュメントの役割を果たす

# Pythonでも型ヒントを使えば
from typing import Literal, Union
from pydantic import BaseModel

class ActiveUser(BaseModel):
    status: Literal['active']
    last_login: datetime

class InactiveUser(BaseModel):
    status: Literal['inactive'] 
    suspended_since: datetime

User = Union[ActiveUser, InactiveUser]

こうしておけば、半年後に引き継いだ後輩エンジニアも、「あ、このUserは状態によって使えるフィールドが違うんだ」ってすぐ分かります。

mypy使ってれば、間違った使い方をした時点でエラーが出るので、バグも防げますね。

レビューも楽になる

型がしっかりしてると、コードレビューの時も

  • 「この関数、何を返すんだっけ?」→型定義見れば分かる
  • 「この引数、nullの可能性は?」→Optional使ってるかどうかで判断
  • 「エラーハンドリング漏れてない?」→Result型使ってれば強制的に処理

みたいな感じで、レビューの負担も減ります。

感想:型システム、もっと真剣に向き合おう

正直、今まで型システムって「あったら便利だけど、面倒な時もあるよね」くらいの認識でした。でもこの記事読んで、考えが変わりました。

AI時代だからこそ、型システムが武器になる

  • AIと協働する時の「共通言語」として機能
  • チーム開発でのコミュニケーションコストを下げる
  • 将来の自分や同僚への「親切」になる

Pythonエンジニアとして思うこと

Pythonは動的型付け言語ですが、型ヒント + mypy + pydanticの組み合わせで、かなり強力な型システムが使えます。

ただし、何でもかんでも型でガチガチに固めればいいってわけでもないと思います。

  • プロトタイピング段階では、柔軟性も必要
  • 過度な型定義は、かえって開発速度を落とす場合も
  • チームのスキルレベルに合わせた導入が重要

まとめ

Matsakis氏の記事から学んだのは、「型システムはAI時代の必須スキル」ってことです。

AIとのペアプログラミングでも、チーム開発でも、型システムがあることで

  • コミュニケーションが円滑になる
  • バグが減る
  • 保守性が向上する

特にPython使ってる方は、型ヒント使ってない場合は一度試してみることをおすすめします!最初は面倒に感じるかもしれませんが、慣れると手放せなくなりますよ。

皆さんは型システム、どの程度活用してますか?コメントで教えてください!

参考記事

Rust, Python, and TypeScript: the new trifecta

おまけ

mypy、型ヒント、pydanticを組み合わせた実用的なサンプルコードを作成しました!(Claudeが)

user_management.py
"""
mypy + 型ヒント + pydantic を使用したユーザー管理システムのサンプル
"""

from datetime import datetime, date
from typing import List, Optional, Union, Dict, Any
from enum import Enum
from pydantic import BaseModel, EmailStr, validator, Field
import json


# Enumで状態を定義
class UserStatus(str, Enum):
    ACTIVE = "active"
    INACTIVE = "inactive"
    BANNED = "banned"


class UserRole(str, Enum):
    ADMIN = "admin"
    USER = "user"
    GUEST = "guest"


# Pydanticモデルの定義
class Address(BaseModel):
    """住所情報"""
    street: str
    city: str
    postal_code: str = Field(..., regex=r'^\d{3}-\d{4}$')  # 郵便番号のバリデーション
    country: str = "Japan"


class User(BaseModel):
    """ユーザー情報"""
    id: int
    name: str = Field(..., min_length=1, max_length=100)
    email: EmailStr
    age: int = Field(..., ge=0, le=150)  # 0以上150以下
    status: UserStatus
    role: UserRole = UserRole.USER
    created_at: datetime
    last_login: Optional[datetime] = None
    address: Optional[Address] = None
    tags: List[str] = []
    
    @validator('age')
    def validate_age(cls, v: int) -> int:
        if v < 0:
            raise ValueError('年齢は0以上である必要があります')
        return v
    
    @validator('name')
    def validate_name(cls, v: str) -> str:
        if not v.strip():
            raise ValueError('名前は空白のみでは設定できません')
        return v.strip()


class LoginRequest(BaseModel):
    """ログインリクエスト"""
    email: EmailStr
    password: str = Field(..., min_length=8)


class LoginResponse(BaseModel):
    """ログインレスポンス"""
    success: bool
    user: Optional[User] = None
    token: Optional[str] = None
    message: str


class UserRepository:
    """ユーザーデータの管理クラス"""
    
    def __init__(self) -> None:
        self._users: Dict[int, User] = {}
        self._next_id: int = 1
    
    def create_user(
        self, 
        name: str, 
        email: str, 
        age: int,
        address: Optional[Dict[str, Any]] = None
    ) -> User:
        """新しいユーザーを作成"""
        user_data = {
            "id": self._next_id,
            "name": name,
            "email": email,
            "age": age,
            "status": UserStatus.ACTIVE,
            "created_at": datetime.now(),
            "address": Address(**address) if address else None
        }
        
        user = User(**user_data)
        self._users[user.id] = user
        self._next_id += 1
        return user
    
    def get_user(self, user_id: int) -> Optional[User]:
        """ユーザーを取得"""
        return self._users.get(user_id)
    
    def get_users_by_status(self, status: UserStatus) -> List[User]:
        """ステータス別にユーザーを取得"""
        return [user for user in self._users.values() if user.status == status]
    
    def update_user_status(self, user_id: int, status: UserStatus) -> bool:
        """ユーザーのステータスを更新"""
        user = self.get_user(user_id)
        if user is None:
            return False
        
        # Pydanticモデルは不変なので、新しいインスタンスを作成
        updated_data = user.dict()
        updated_data['status'] = status
        self._users[user_id] = User(**updated_data)
        return True
    
    def get_all_users(self) -> List[User]:
        """全ユーザーを取得"""
        return list(self._users.values())


class UserService:
    """ユーザー関連のビジネスロジック"""
    
    def __init__(self, repository: UserRepository) -> None:
        self.repository = repository
    
    def register_user(
        self, 
        name: str, 
        email: str, 
        age: int,
        address_data: Optional[Dict[str, str]] = None
    ) -> Union[User, str]:
        """ユーザー登録(バリデーション付き)"""
        try:
            # 既存ユーザーのチェック
            existing_users = self.repository.get_all_users()
            for user in existing_users:
                if user.email == email:
                    return "このメールアドレスは既に使用されています"
            
            # ユーザー作成
            user = self.repository.create_user(name, email, age, address_data)
            return user
            
        except Exception as e:
            return f"ユーザー登録に失敗しました: {str(e)}"
    
    def login(self, request: LoginRequest) -> LoginResponse:
        """ログイン処理"""
        # 実際のパスワード認証はここでは省略
        users = self.repository.get_all_users()
        user = next((u for u in users if u.email == request.email), None)
        
        if user is None:
            return LoginResponse(
                success=False,
                message="ユーザーが見つかりません"
            )
        
        if user.status == UserStatus.BANNED:
            return LoginResponse(
                success=False,
                message="このアカウントは凍結されています"
            )
        
        # ログイン成功時の処理
        return LoginResponse(
            success=True,
            user=user,
            token="dummy-jwt-token",
            message="ログインに成功しました"
        )
    
    def get_user_statistics(self) -> Dict[str, Any]:
        """ユーザー統計情報を取得"""
        users = self.repository.get_all_users()
        
        status_count: Dict[UserStatus, int] = {}
        for status in UserStatus:
            status_count[status] = len([u for u in users if u.status == status])
        
        total_users = len(users)
        average_age = sum(user.age for user in users) / total_users if users else 0
        
        return {
            "total_users": total_users,
            "status_breakdown": {status.value: count for status, count in status_count.items()},
            "average_age": round(average_age, 1),
            "users_with_address": len([u for u in users if u.address is not None])
        }


def demonstrate_usage() -> None:
    """使用例のデモンストレーション"""
    
    # リポジトリとサービスの初期化
    repository = UserRepository()
    service = UserService(repository)
    
    print("=== ユーザー管理システム デモ ===\n")
    
    # ユーザー登録
    print("1. ユーザー登録")
    users_data = [
        {
            "name": "田中太郎",
            "email": "tanaka@example.com",
            "age": 30,
            "address": {
                "street": "渋谷1-1-1",
                "city": "東京都渋谷区",
                "postal_code": "150-0002"
            }
        },
        {
            "name": "佐藤花子",
            "email": "sato@example.com", 
            "age": 25
        },
        {
            "name": "鈴木一郎",
            "email": "suzuki@example.com",
            "age": 35
        }
    ]
    
    for user_data in users_data:
        result = service.register_user(
            name=user_data["name"],
            email=user_data["email"],
            age=user_data["age"],
            address_data=user_data.get("address")
        )
        if isinstance(result, User):
            print(f"{result.name} を登録しました (ID: {result.id})")
        else:
            print(f"✗ 登録失敗: {result}")
    
    print()
    
    # ログインテスト
    print("2. ログインテスト")
    login_request = LoginRequest(email="tanaka@example.com", password="password123")
    login_result = service.login(login_request)
    
    if login_result.success:
        print(f"✓ ログイン成功: {login_result.user.name}")
    else:
        print(f"✗ ログイン失敗: {login_result.message}")
    
    print()
    
    # ユーザー状態更新
    print("3. ユーザー状態更新")
    repository.update_user_status(2, UserStatus.INACTIVE)
    print("✓ ユーザーID 2 を非アクティブに変更")
    
    print()
    
    # 統計情報表示
    print("4. ユーザー統計")
    stats = service.get_user_statistics()
    print(f"総ユーザー数: {stats['total_users']}")
    print(f"平均年齢: {stats['average_age']}")
    print(f"住所登録済み: {stats['users_with_address']}")
    print("ステータス別:")
    for status, count in stats['status_breakdown'].items():
        print(f"  {status}: {count}")
    
    print()
    
    # JSON出力例
    print("5. JSON出力例")
    active_users = repository.get_users_by_status(UserStatus.ACTIVE)
    if active_users:
        user_json = active_users[0].json(indent=2, ensure_ascii=False)
        print("アクティブユーザーの例:")
        print(user_json)


if __name__ == "__main__":
    demonstrate_usage()

    # mypy チェック用のコメント
    # このファイルをチェックするには:
    # pip install mypy pydantic[email]
    # mypy user_management.py
14
5
1

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
14
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?