最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

rust - Module and traits in separates files, traits are out of scope in main function - Stack Overflow

programmeradmin5浏览0评论

I'm trying to learn Rust but I'm stucked with a simple "problem".

It's a super basic program that explores modules, traits and the principles of OOP. In other languages is a straight forward thing to do, but in Rust i'm stucked with. I overcomplicated the program with string slices instead of String, just for "fun".

Basically, it's a program that create a Car class (struct) that inherit some functionalities from Vehicle and StartAndStoppableVehicle traits.

It sounds pretty simple, right?? Well, When I try to compile it, it appears this error:

omiss...items from traits can only be used if the trait is in scope

This is the question: Is there a way to import the Car class only with all the inherited funcionalities, without importing the traits manually in the main function?

It seemed simple to me but instead...

src/cars/vehicle.rs

pub trait Vehicle {
    fn turn_on(&mut self);
    fn turn_off(&mut self);
    fn start(&mut self);
    fn stop(&mut self);
}

pub trait StartAndStoppableVehicle:Vehicle {
    fn start_and_stop(&mut self);
}

src/cars/cars.rs

use core::fmt;
use super::vehicle::{StartAndStoppableVehicle, Vehicle};

pub enum CarStatus {
    On,
    Off,
    Started,
    Stopped,
    StartedAndStopped 
}

impl fmt::Display for CarStatus {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            CarStatus::On => write!(f, "On"),
            CarStatus::Off => write!(f, "Off"),
            CarStatus::Started => write!(f, "Started"),
            CarStatus::Stopped => write!(f, "Stopped"),
            CarStatus::StartedAndStopped => write!(f, "StartedAndStopped")
        }
    }
}

pub struct Car<'a> {
    brand: &'a str,
    model: &'a str,
    pub complete_name: String,
    pub status: CarStatus
}

impl<'a> Car<'a> {
    pub fn new(brand: &'a str, model: &'a str) -> Car<'a> {
        Car {
            brand: brand,
            model: model,
            complete_name: format!("{} {}", brand, model),
            status: CarStatus::Off
        }
    }
}

impl<'a> Vehicle for Car<'a> {
    fn turn_on(&mut self){
        self.status = CarStatus::On;
    }

    fn turn_off(&mut self){
        self.status = CarStatus::Off;
    }
    fn start(&mut self){
        self.status = CarStatus::Started;
    }

    fn stop(&mut self){
        self.status = CarStatus::Stopped;
    }
}

impl<'a> StartAndStoppableVehicle for Car<'a> {
    fn start_and_stop(&mut self) {
        self.status = CarStatus::StartedAndStopped;
    }
}

src/cars/mod.rs

pub mod car;
pub mod vehicle;

src/main.rs

mod car;
use car::car::Car;

// use car::vehicle::{StartAndStoppableVehicle, Vehicle}; <- it works only with this! 
// And i dont want it

fn main() {

    let brand = "Ferrari";
    let model = "SF90 Stradale";
    let mut car = Car::new(brand, model);
    
    println!("{}", carplete_name);
    println!("{}", car.status);
    car.start();
    println!("{}", car.status);
    car.stop();
    println!("{}", car.status);
    car.start_and_stop();
    println!("{}", car.status);

}

I'm trying to learn Rust but I'm stucked with a simple "problem".

It's a super basic program that explores modules, traits and the principles of OOP. In other languages is a straight forward thing to do, but in Rust i'm stucked with. I overcomplicated the program with string slices instead of String, just for "fun".

Basically, it's a program that create a Car class (struct) that inherit some functionalities from Vehicle and StartAndStoppableVehicle traits.

It sounds pretty simple, right?? Well, When I try to compile it, it appears this error:

omiss...items from traits can only be used if the trait is in scope

This is the question: Is there a way to import the Car class only with all the inherited funcionalities, without importing the traits manually in the main function?

It seemed simple to me but instead...

src/cars/vehicle.rs

pub trait Vehicle {
    fn turn_on(&mut self);
    fn turn_off(&mut self);
    fn start(&mut self);
    fn stop(&mut self);
}

pub trait StartAndStoppableVehicle:Vehicle {
    fn start_and_stop(&mut self);
}

src/cars/cars.rs

use core::fmt;
use super::vehicle::{StartAndStoppableVehicle, Vehicle};

pub enum CarStatus {
    On,
    Off,
    Started,
    Stopped,
    StartedAndStopped 
}

impl fmt::Display for CarStatus {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            CarStatus::On => write!(f, "On"),
            CarStatus::Off => write!(f, "Off"),
            CarStatus::Started => write!(f, "Started"),
            CarStatus::Stopped => write!(f, "Stopped"),
            CarStatus::StartedAndStopped => write!(f, "StartedAndStopped")
        }
    }
}

pub struct Car<'a> {
    brand: &'a str,
    model: &'a str,
    pub complete_name: String,
    pub status: CarStatus
}

impl<'a> Car<'a> {
    pub fn new(brand: &'a str, model: &'a str) -> Car<'a> {
        Car {
            brand: brand,
            model: model,
            complete_name: format!("{} {}", brand, model),
            status: CarStatus::Off
        }
    }
}

impl<'a> Vehicle for Car<'a> {
    fn turn_on(&mut self){
        self.status = CarStatus::On;
    }

    fn turn_off(&mut self){
        self.status = CarStatus::Off;
    }
    fn start(&mut self){
        self.status = CarStatus::Started;
    }

    fn stop(&mut self){
        self.status = CarStatus::Stopped;
    }
}

impl<'a> StartAndStoppableVehicle for Car<'a> {
    fn start_and_stop(&mut self) {
        self.status = CarStatus::StartedAndStopped;
    }
}

src/cars/mod.rs

pub mod car;
pub mod vehicle;

src/main.rs

mod car;
use car::car::Car;

// use car::vehicle::{StartAndStoppableVehicle, Vehicle}; <- it works only with this! 
// And i dont want it

fn main() {

    let brand = "Ferrari";
    let model = "SF90 Stradale";
    let mut car = Car::new(brand, model);
    
    println!("{}", carplete_name);
    println!("{}", car.status);
    car.start();
    println!("{}", car.status);
    car.stop();
    println!("{}", car.status);
    car.start_and_stop();
    println!("{}", car.status);

}

Share Improve this question edited Mar 25 at 23:06 John Kugelman 362k69 gold badges552 silver badges596 bronze badges asked Mar 25 at 19:58 Alessandro CorradiniAlessandro Corradini 5011 gold badge9 silver badges29 bronze badges 1
  • 5 You're making a mistake trying to force OOP mind into Rust. – Chayim Friedman Commented Mar 25 at 21:25
Add a comment  | 

3 Answers 3

Reset to default 4

Unless you want to call the trait methods with fully-qualified associated function call syntax (car::vehicle::Vehicle::stop(&mut car)), you need the use line that you say you don't want. The rule is that you can only call trait functions with method syntax if that trait is in scope. This is necessary otherwise someone adding a trait anywhere in any crate you use has the potential to suddenly inject into your scope new methods on potentially any type. One of the benefits of requiring that you import traits you use is that the use directives show all possible places (excluding the Rust prelude) where additional methods could come from.

One workaround could be to include the trait methods on the implementing type as well, e.g. have:

impl Car<'_> {
    fn stop(&mut self) {
        Vehicle::stop(self)
    }

    // And so on...
}

Obviously this would be a lot of boilerplate to save you from simply importing the traits you're trying to use.

My suggestion would be to work with the grain instead of against it -- accept that you need to import the trait with use. Anything else I would call unidiomatic Rust.

Rust doesn’t allow methods from traits to be automatically available just because the struct implements those traits.

In your example, the traits need to be explicitly imported into the scope of your main function to use the methods defined in them.

Re-exporting the traits with the Car struct allows you to bundle the Car struct and the relevant traits into a single module that is used in your main.rs

main.rs

mod cars;

use cars::{Car, Vehicle, StartAndStoppableVehicle}; // Single scope for everything

fn main() {
    let brand = "Ferrari";
    let model = "SF90 Stradale";
    let mut car = Car::new(brand, model);

    println!("{}", carplete_name);
    println!("{}", car.status);
    car.start();
    println!("{}", car.status);
    car.stop();
    println!("{}", car.status);
    car.start_and_stop();
    println!("{}", car.status);
}

car.rs

use core::fmt;
use super::vehicle::{StartAndStoppableVehicle, Vehicle};

pub enum CarStatus {
    On,
    Off,
    Started,
    Stopped,
    StartedAndStopped,
}

impl fmt::Display for CarStatus {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            CarStatus::On => write!(f, "On"),
            CarStatus::Off => write!(f, "Off"),
            CarStatus::Started => write!(f, "Started"),
            CarStatus::Stopped => write!(f, "Stopped"),
            CarStatus::StartedAndStopped => write!(f, "StartedAndStopped"),
        }
    }
}

pub struct Car<'a> {
    brand: &'a str,
    model: &'a str,
    pub complete_name: String,
    pub status: CarStatus,
}

impl<'a> Car<'a> {
    pub fn new(brand: &'a str, model: &'a str) -> Car<'a> {
        Car {
            brand,
            model,
            complete_name: format!("{} {}", brand, model),
            status: CarStatus::Off,
        }
    }
}

impl<'a> Vehicle for Car<'a> {
    fn turn_on(&mut self) {
        self.status = CarStatus::On;
    }

    fn turn_off(&mut self) {
        self.status = CarStatus::Off;
    }

    fn start(&mut self) {
        self.status = CarStatus::Started;
    }

    fn stop(&mut self) {
        self.status = CarStatus::Stopped;
    }
}

impl<'a> StartAndStoppableVehicle for Car<'a> {
    fn start_and_stop(&mut self) {
        self.status = CarStatus::StartedAndStopped;
    }
}

mod.rs

pub mod car;
pub mod vehicle;
pub use car::Car; // Re-export the Car struct
pub use vehicle::{Vehicle, StartAndStoppableVehicle}; // Re-export both traits

In Rust, trait methods can only be accessed when they're in scope. This applies on whether they're user defined, or whether they come as part of the standard library, or some external crate.

发布评论

评论列表(0)

  1. 暂无评论