How to reuse common codes for structs?

ぐ巨炮叔叔 提交于 2021-02-11 01:53:03

问题


I want to reuse codes for structs. For example:

use std::fmt::Display;

struct CommonStruct<T: Display> {
    // could have more fields
    data: T
}

struct A<T: Display> {
    com: CommonStruct<T>,
    age: i32
}

struct B<T: Display> {
    com: CommonStruct<T>,
    name: String
}

impl<T: Display> A<T> {
    // could be more common functions
    fn print_data(&self) {
        // could be more complicated
        println!("data: {}", self.com.data);
    }
}

impl<T: Display> B<T> {
    // could be more common functions
    fn print_data(&self) {
        // could be more complicated
        println!("data: {}", self.com.data);
    }
}

fn main() {
    let a = A{ com: CommonStruct{data: 10}, age: 0 };
    a.print_data();
    let b = B{ com: CommonStruct{data: 12}, name: "123".to_string() };
    b.print_data();
}

where A and B have some common fields packed by CommonStruct and some common functions (e.g., print_data).

I tried to use trait but cannot figure out a solution:

use std::fmt::Display;

struct CommonStruct<T: Display> {
    // could have more fields
    data: T
}

struct A<T: Display> {
    com: CommonStruct<T>,
    age: i32
}

struct B<T: Display> {
    com: CommonStruct<T>,
    name: String
}

trait Common {
    // could be more common functions
    fn print_data(&self) {
        print_data(&self)
    }
}

impl<T: Display> Common for A<T> {
}

impl<T: Display> Common for B<T> {
}

fn print_data(t: &Common) {
    // could be more complicated
    println!("data: {}", t.com.data);
}

fn main() {
    let a = A{ com: CommonStruct{data: 10}, age: 0 };
    a.print_data();
    let b = B{ com: CommonStruct{data: 12}, name: "123".to_string() };
    b.print_data();
}

回答1:


Since print_data only uses the CommonStruct, and A and B share no other fields, make it an implementation of CommonStruct and call it directly.

impl <T: Display> CommonStruct<T> {
    fn print_data(&self) {
        println!("data: {}", self.data);
    }
}

fn main() {
    let a = A{ com: CommonStruct{data: 10}, age: 0 };
    a.com.print_data();
    let b = B{ com: CommonStruct{data: 12}, name: "123".to_string() };
    b.com.print_data();
}

Alternatively, make a trait which has a concrete implementation of print_data which relies on a method to get the data.

trait HasData<T: Display> {
    fn get_data(&self) -> &T;
    fn print_data(&self) {
        // could be more complicated
        println!("data: {}", self.get_data());
    }
}

Then each only has to implement how to get the data.

impl<T: Display> HasData<T> for CommonStruct<T> {
    fn get_data(&self) -> &T {
        return &self.data;
    }
}

impl<T: Display> HasData<T> for A<T> {
    fn get_data(&self) -> &T {
        return &self.com.data;
    }
}

impl<T: Display> HasData<T> for B<T> {
    fn get_data(&self) -> &T {
        return &self.com.data;
    }
}

fn main() {
    let a = A{ com: CommonStruct{data: 1}, age: 0 };
    a.print_data();
    let b = B{ com: CommonStruct{data: 2}, name: "123".to_string() };
    b.print_data();
    let c = CommonStruct{data: 3};
    c.print_data();
}



回答2:


I saw that you wanted to use traits. I've included a working example below.

However, traits are used to implement shared behavior. A trait must be implemented on each type you wish to have the shared behavior. So, you do have to impl a separate print_data function for a given type.

use std::fmt::Display;

// `Common` structure of some type `T`.
struct Common<T: Display> {
    data: T,
}

// `Printable` trait used to print `data`.
trait Printable {
    fn print_data(&self);
}

struct A {
    common: Common<String>,
}

impl A {
    fn new(s: &str) -> A {
        A {
            common: Common { data: s.to_owned() },
        }
    }
}

// Implement `Printable the trait for A`.
impl Printable for A {
    fn print_data(&self) {
        println!("A.common.data: {}", self.common.data)
    }
}

struct B {
    common: Common<i32>,
}

// Implement the `Printable` trait for `B`.
impl Printable for B {
    fn print_data(&self) {
        println!("B.common.data: {}", self.common.data)
    }
}

So that's traits, but if you must call the same function to print the data, then maybe something like the following can work for you. It defines an enum with three variants. You can then match on a particular variant as demonstrated by print_all_data.

use std::path::PathBuf;
struct G {
    path: PathBuf,
    common: Common<String>,
}

enum C {
    D(A),
    E(B),
    F(G),
}

fn print_all_data(c: C) {
    match c {
        C::D(a) => println!("{}", a.common.data),
        C::E(b) => println!("{}", b.common.data),
        C::F(g) => println!("{} {:?}", g.common.data, g.path)
    }
}

fn main() {
    let a = A::new("hello");
    a.print_data();

    let b = B {
        common: Common { data: 42 },
    };
    b.print_data();

    let g = G {
        path: PathBuf::from("some/path/file.txt"),
        common: Common {data: "some_data".to_owned()}
    };

    let cfg = C::F(g);
    print_all_data(cfg);
    print_all_data(C::D(a));
    print_all_data(C::E(b));
}


来源:https://stackoverflow.com/questions/64733353/how-to-reuse-common-codes-for-structs

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!