Portal/Notes πŸ“
RustRustlings_exercise

Traits

Versi Bahasa Indonesia. Baca dalam Bahasa Inggris

  • Trait mirip dengan interface di bahasa pemrograman lain.
  • Ia mendefinisikan sekumpulan metode yang harus diimplementasikan oleh suatu tipe, memungkinkan kamu menentukan fungsionalitas apa yang disediakan suatu tipe tanpa mendikte bagaimana ia menyediakannya.
  • Mengimplementasikan trait pada suatu tipe mirip dengan mengimplementasikan metode reguler.
  • Perbedaannya adalah setelah impl, kita letakkan nama trait yang ingin kita implementasikan, lalu gunakan keyword for, dan kemudian tentukan nama tipe yang ingin kita implementasikan trait tersebut.
  • Traits dapat menyediakan implementasi metode default yang bisa di-override oleh tipe.
  • Sintaks impl Trait berfungsi untuk kasus yang sederhana tetapi sebenarnya adalah sintaks gula untuk bentuk yang lebih panjang yang dikenal sebagai trait bound.
  • Kita juga dapat menentukan lebih dari satu trait bound menggunakan sintaks +.
  • Referensi:

traits1.rs

// The trait `AppendBar` has only one function which appends "Bar" to any object
// implementing this trait.
trait AppendBar {
    fn append_bar(self) -> Self;
}

impl AppendBar for String {
    // Implement `AppendBar` for the type `String`.
    fn append_bar(self) -> Self {
        self + "Bar"
    }
}

fn main() {
    let s = String::from("Foo");
    let s = s.append_bar();
    println!("s: {s}");
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn is_foo_bar() {
        assert_eq!(String::from("Foo").append_bar(), "FooBar");
    }

    #[test]
    fn is_bar_bar() {
        assert_eq!(String::from("").append_bar().append_bar(), "BarBar");
    }
}
  • Latihan ini mudah, kita hanya perlu mengimplementasikan item/fungsi trait append_bar di dalam block impl AppendBar for String.

traits2.rs

trait AppendBar {
    fn append_bar(self) -> Self;
}

// Implement the trait `AppendBar` for a vector of strings.
// `append_bar` should push the string "Bar" into the vector.
impl AppendBar for Vec<String> {
    fn append_bar(mut self) -> Self {
        self.push("Bar".to_string());
        self
    }
}

fn main() {
    // You can optionally experiment here.
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn is_vec_pop_eq_bar() {
        let mut foo = vec![String::from("Foo")].append_bar();
        assert_eq!(foo.pop().unwrap(), "Bar");
        assert_eq!(foo.pop().unwrap(), "Foo");
    }
}
  • Latihan ini juga mirip dengan yang pertama.

  • Kita perlu membuat implementasi trait AppendBar untuk Vec<String>.

  • Karena kita ingin memutasi vector (self) kita perlu menambahkan sintaks mut.

  • Implementasi lengkapnya akan terlihat seperti ini:

    impl AppendBar for Vec<String> {
        fn append_bar(mut self) -> Self {
            self.push("Bar".to_string());
            self
        }
    }

traits3.rs

trait Licensed {
    // Add a default implementation for `licensing_info` so that
    // implementors like the two structs below can share that default behavior
    // without repeating the function.
    // The default license information should be the string "Default license".
    fn licensing_info(&self) -> String {
        "Default license".to_string()
    }
}

struct SomeSoftware {
    version_number: i32,
}

struct OtherSoftware {
    version_number: String,
}

impl Licensed for SomeSoftware {} // Don't edit this line.
impl Licensed for OtherSoftware {} // Don't edit this line.

fn main() {
    // You can optionally experiment here.
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn is_licensing_info_the_same() {
        let licensing_info = "Default license";
        let some_software = SomeSoftware { version_number: 1 };
        let other_software = OtherSoftware {
            version_number: "v2.0.0".to_string(),
        };
        assert_eq!(some_software.licensing_info(), licensing_info);
        assert_eq!(other_software.licensing_info(), licensing_info);
    }
}
  • Di latihan ini kita perlu membuat implementasi default dari licensing_info yang akan mengembalikan "Default license".
  • Implementasi default ini akan dipanggil jika trait tidak diimplementasikan untuk tipe yang ditentukan.

traits4.rs

trait Licensed {
    fn licensing_info(&self) -> String {
        "Default license".to_string()
    }
}

struct SomeSoftware;
struct OtherSoftware;

impl Licensed for SomeSoftware {}
impl Licensed for OtherSoftware {}

// Fix the compiler error by only changing the signature of this function.
fn compare_license_types(software1: impl Licensed, software2: impl Licensed) -> bool {
    software1.licensing_info() == software2.licensing_info()
}

fn main() {
    // You can optionally experiment here.
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn compare_license_information() {
        assert!(compare_license_types(SomeSoftware, OtherSoftware));
    }

    #[test]
    fn compare_license_information_backwards() {
        assert!(compare_license_types(OtherSoftware, SomeSoftware));
    }
}
  • Di latihan ini kita perlu mengubah signature fungsi compare_license_types untuk menerima tipe SomeSoftware dan OtherSoftware.

  • Kita dapat melakukannya menggunakan generics.

  • Atau kita juga dapat menggunakan sintaks impl Trait seperti ini:

    fn compare_license_types(software1: impl Licensed, software2: impl Licensed) -> bool
  • Kamu dapat membaca lebih lanjut di sini: https://doc.rust-lang.org/book/ch10-02-traits.html#trait-bound-syntax

traits5.rs

trait SomeTrait {
    fn some_function(&self) -> bool {
        true
    }
}

trait OtherTrait {
    fn other_function(&self) -> bool {
        true
    }
}

struct SomeStruct;
impl SomeTrait for SomeStruct {}
impl OtherTrait for SomeStruct {}

struct OtherStruct;
impl SomeTrait for OtherStruct {}
impl OtherTrait for OtherStruct {}

// Fix the compiler error by only changing the signature of this function.
fn some_func(item: (impl SomeTrait + OtherTrait)) -> bool {
    item.some_function() && item.other_function()
}

fn main() {
    // You can optionally experiment here.
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_some_func() {
        assert!(some_func(SomeStruct));
        assert!(some_func(OtherStruct));
    }
}
  • Di latihan ini kita perlu memperbarui signature fungsi some_func untuk menerima implementasi dari SomeTrait dan OtherTrait.

  • Ini berarti tipe yang diberikan harus mengimplementasikan kedua trait.

  • Kita dapat melakukannya dengan menggunakan sintaks + seperti ini:

    fn some_func(item: (impl SomeTrait + OtherTrait)) -> bool

Edit on GitHub

Last updated on

Traits | Faisal Affan