0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Effective-Rustlings-jp】Day 5:型変換を理解しよう

Last updated at Posted at 2025-01-05

はじめに

こんにちは。細々とプログラミングをしているsotanengelです。
この記事は以下の記事の連載です。

他の連載記事 (詳細)

また本記事はEffective Rust(David Drysdale (著), 中田 秀基 (翻訳))を参考に作成されております。とてもいい書籍ですので興味を持った方は、ぜひ読んでみてください!

今日の内容

概要

Rustには以下の3つの型変換があります。
これらの変換のうち、ユーザーが定義した型に対する変換処理を実装します。

  1. 手動
    1. ユーザーが実装するFromIntoのこと
  2. 半自動
    1. asによる明示的なキャスト
  3. 自動
    1. 自動型変換

自作の型にFromトレイトを実装する

問(リンク)

RustではFromトレイトを実装することでIntoも自動的に実装されます。
MyStructFromトレイトを実装しましょう。

コード (詳細)
#[derive(Debug)]
#[warn(dead_code)]
struct MyStruct {
    value: String,
}

// TODO: MyStructにFrom<String>トレイトを実装して、main内部でintoが使えるようにしてください。

fn main() {
    let s = String::from("Hello");
    let my_struct: MyStruct = s.into();
    println!("{:?}", my_struct);
}

解答(リンク)

コード参照。

コード (詳細)
#[derive(Debug)]
#[allow(dead_code)]
struct MyStruct {
    value: String,
}

// Fromトレイトを実装することでIntoが自動実行されます。
impl From<String> for MyStruct {
    fn from(s: String) -> Self {
        MyStruct { value: s }
    }
}

fn main() {
    let s = String::from("Hello");
    let my_struct: MyStruct = s.into();
    println!("{:?}", my_struct);
}

トレイト制約にはIntoを使おう

問(リンク)

print_length内部で引数をIntoで変更しましょう。

コード (詳細)
fn print_length<T: Into<String>>(value: T) {
    // TODO: intoを使って、valueを変換した値を変数sに格納してください。
    println!("Length: {}", s.len());
}

fn main() {
    let s = "Hello".to_string();
    let str_slice = "Goodbye";

    print_length(s);
    print_length(str_slice);
}

解答(リンク)

Intoを使うことで以下の両方に対応することができます。

  • Fromトレイトを実装しているもの
  • Intoトレイトを実装しているもの
コード (詳細)
fn print_length<T: Into<String>>(value: T) {
    let s: String = value.into(); // トレイト制約を実行する際にはIntoを使う。
    println!("Length: {}", s.len());
}

fn main() {
    let s = "Hello".to_string();
    let str_slice = "Goodbye";

    print_length(s);
    print_length(str_slice);
}

反射実装をする

問(リンク)

反射実装を使ってジェネリック関数での変換を省略しましょう。

コード (詳細)
// ドメイン名を表す構造体
#[derive(Debug)]
pub struct DomainName(pub String);

// StringからDomainNameへの変換
impl From<String> for DomainName {
    fn from(value: String) -> Self {
        Self(value)
    }
}

// &str からも直接変換できるようにする
impl From<&str> for DomainName {
    fn from(value: &str) -> Self {
        Self(value.to_string())
    }
}

// ドメインが example.com のサブドメインかを判定する関数
// TODO: 反射実装を使って、この関数にトレイト制約をInto<DomainName>に課すことで
//    「String」「&str」「DomainName」を引数に入れたとしても対応できるようにしてください。
pub fn is_example_subdomain<T>(domain: T) -> bool {
    let domain = domain.into();
    domain.0.ends_with(".example.com")
}

fn main() {
    let domain1 = DomainName("sub.example.com".to_string());
    let domain2 = "other.com".to_string();
    let domain3 = "api.example.com";

    // DomainName構造体を直接渡す
    println!("sub.example.com? {}", is_example_subdomain(domain1));

    // Stringを渡す (Intoで自動変換)
    println!("other.com? {}", is_example_subdomain(domain2));

    // &strを渡す (Intoで自動変換)
    println!("api.example.com? {}", is_example_subdomain(domain3));
}

解答(リンク)

コード参照。

コード (詳細)
// ドメイン名を表す構造体
#[derive(Debug)]
pub struct DomainName(pub String);

// StringからDomainNameへの変換
impl From<String> for DomainName {
    fn from(value: String) -> Self {
        Self(value)
    }
}

// &str からも直接変換できるようにする
impl From<&str> for DomainName {
    fn from(value: &str) -> Self {
        Self(value.to_string())
    }
}

// ドメインが example.com のサブドメインかを判定する関数
pub fn is_example_subdomain<T>(domain: T) -> bool
where
    // ジェネリック T を受け取り、T が Into<DomainName> トレイトを実装していれば受け入れます。
    // つまりString や &str など DomainName に変換可能な型を引数に渡せます。
    // またRustは自身への変換 (Into<DomainName>) は自動で実装されています。
    T: Into<DomainName>,
{
    let domain = domain.into();
    domain.0.ends_with(".example.com")
}

fn main() {
    let domain1 = DomainName("sub.example.com".to_string());
    let domain2 = "other.com".to_string();
    let domain3 = "api.example.com";

    // DomainName構造体を直接渡す
    println!("sub.example.com? {}", is_example_subdomain(domain1));

    // Stringを渡す (Intoで自動変換)
    println!("other.com? {}", is_example_subdomain(domain2));

    // &strを渡す (Intoで自動変換)
    println!("api.example.com? {}", is_example_subdomain(domain3));
}

さいごに

もしも本リポジトリで不備などあれば、リポジトリのissueやPRなどでご指摘いただければと思います。

0
0
0

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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?