first of all the obligatory "I am new to Rust": I am.
So I have the following problem:
I have two(or more) structs of data, that all implement some common behaviour in addition their own behaviour. I have a list of these structs (or rather: of the 'supertype'), I need to access some of their shared behaviour and some of their individual behaviour.
My Question is: how do I do that in Rust.
To further illustrate my question I have come up with a code comparision between Kotlin and Rust. Kotlin works as I want it to, Rust does not (yet).
In Kotlin the code may look like this(using the plain, old inheritance abstraction):
interface Animal {
fun eat()
fun sleep()
}
class Cat(val name: String) : Animal {
fun meow() { println("meow") }
override fun eat() { println("cat $name is eating fish(or lasagne)") }
override fun sleep() { println("cat $name sleeps inside") }
}
class Lion(val tag_id: Int) : Animal {
fun roar() { println("roar") }
override fun eat() { println("lion(tag=${tag_id} is eating gazelle") }
override fun sleep() { println("lion(tag=${tag_id} sleeps outside") }
}
var animals: MutableList<Animal> = ArrayList()
fun main() {
animals.add(Cat("Garfield"))
animals.add(Lion(12))
//later:
for (animal in animals) {
animal.sleep()
if (animal is Lion)
animal.roar()
}
}
In Rust I came up with the following code (which does not allow an 'instance_of' type function):
trait Animal {
fn eat(&self);
fn sleep(&self);
}
struct Cat {
name: String
}
impl Cat {
fn meow(&self) { println!("meow") }
}
impl Animal for Cat {
fn eat(&self) { println!("cat {} is eating fish(or lasagne)", self.name) }
fn sleep(&self) { println!("cat {} sleeps inside", self.name) }
}
struct Lion {
tag_id: usize
}
impl Lion {
fn roar(&self) { println!("roar") }
}
impl Animal for Lion {
fn eat(&self) { println!("lion(tag={}) is eating fish(or lasagne)", self.tag_id) }
fn sleep(&self) { println!("lion(tag={}) sleeps inside", self.tag_id) }
}
fn main() {
let animals:Vec<Box<dyn Animal>> = vec![
Box::new(Cat {name: "Garfield".to_string()}),
Box::new(Lion {tag_id: 12})
];
//later:
for animal in animals {
animal.sleep()
//HOW DO I ACCESS THE CONCRETE STRUCT HERE?
}
}
Playground
I realize this may be a stupid question, or show how I am 'still trapped in the non-Rust thinking', but I am at kind of an Impass here and need just a little bit of assistence.
You can try something like this
use std::any::Any;
trait Animal {
fn eat(&self);
fn sleep(&self);
fn as_any(&self) -> &dyn Any;
}
struct Cat {
name: String
}
impl Cat {
fn meow(&self) { println!("meow") }
}
impl Animal for Cat {
fn eat(&self) { println!("cat {} is eating fish(or lasagne)", self.name) }
fn sleep(&self) { println!("cat {} sleeps inside", self.name) }
fn as_any(&self) -> &dyn Any { self }
}
struct Lion {
tag_id: usize
}
impl Lion {
fn roar(&self) { println!("roar") }
}
impl Animal for Lion {
fn eat(&self) { println!("lion(tag={}) is eating fish(or lasagne)", self.tag_id) }
fn sleep(&self) { println!("lion(tag={}) sleeps inside", self.tag_id) }
fn as_any(&self) -> &dyn Any { self }
}
fn main() {
let animals:Vec<Box<dyn Animal>> = vec![
Box::new(Cat {name: "Garfield".to_string()}),
Box::new(Lion {tag_id: 12})
];
//later:
for animal in animals.iter() {
animal.sleep();
if let Some(animal) = animal.as_any().downcast_ref::<Lion>() {
animal.roar();
}
}
}
Try doing it with composition instead
trait Animal {
fn voicebox(&self) -> Voicebox;
}
enum Voicebox {
CatVoicebox, LionVoicebox
}
impl Voicebox {
fn make_sound(&self) {
match *self {
Voicebox::CatVoicebox => println!("meow"),
Voicebox::LionVoicebox => println!("roar!")
}
}
}
impl Animal for Cat {
fn voicebox(&self) -> Voicebox {
Voicebox::CatVoicebox
}
}
impl Animal for Lion {
fn voicebox(&self) -> Voicebox {
Voicebox::LionVoicebox
}
}
fn main() {
let animals:Vec<Box<dyn Animal>> = vec![Box::new(Cat {name: "Garfield".to_string()}), Box::new(Lion {tag_id: 12})];
//later:
for animal in animals {
animal.sleep();
match animal.voicebox() {
vb#Voicebox::LionVoicebox => vb.make_sound(),
_ => ()
}
}
}
Output:
cat Garfield sleeps inside
lion(tag=12) sleeps inside
roar!
Rust playground
Type checked conditions can be implemented by providing a as_<type> function for every subtype:
trait Animal {
fn eat(&self);
fn sleep(&self);
fn as_roaring(&self)->Option<&dyn Roaring>;
fn as_meowing(&self)->Option<&dyn Meowing>;
}
The implementation of Lion would look like:
impl Animal for Lion {
fn eat(&self) { println!("lion(tag={}) is eating fish(or lasagne)", self.tag_id) }
fn sleep(&self) { println!("lion(tag={}) sleeps inside", self.tag_id) }
fn as_roaring(&self)->Option<&dyn Roaring> {Some(self)}
fn as_meowing(&self)->Option<&dyn Meowing> {None}
}
and of the loop:
for animal in animals {
animal.sleep();
if let Some(roaring) = animal.as_roaring() {
roaring.roar();
}
}
Playground.
Personally, I prefer to avoid the use of any or Box<dyn X>, prefering explicit enum wrapper. Often you get a tiny bit of boilerplate per type but I find that it does give additional type-safety and performance.
In this case I'd use an explicit enum Animal wrapping two types, struct Cat and Struct Lion that each implement a trait AnimalCore.
Then when I have an Animal and I want to use the shared behaviour I can just do animal.eat(), and if I want Lion specific behaviour I can pattern match on the enum like this: if let Animal::Lion(lion) = animal { lion.roar() }.
A complete implementation would look like this:
trait AnimalCore {
fn eat(&self);
fn sleep(&self);
}
struct Cat {
name: String,
}
impl Cat {
fn meow(&self) {
println!("meow");
}
}
impl AnimalCore for Cat {
fn eat(&self) {
println!("cat {} is eating fish(or lasagne)", self.name);
}
fn sleep(&self) {
println!("cat {} sleeps inside", self.name);
}
}
struct Lion {
tag_id: i64,
}
impl Lion {
fn roar(&self) {
println!("roar");
}
}
impl AnimalCore for Lion {
fn eat(&self) {
println!("lion(tag={}) is eating gazelle", self.tag_id)
}
fn sleep(&self) {
println!("lion(tag={}) sleeps outside", self.tag_id)
}
}
enum Animal {
Cat(Cat),
Lion(Lion),
}
impl Animal {
fn as_core(&self) -> &dyn AnimalCore {
match self {
Animal::Cat(v) => v,
Animal::Lion(v) => v,
}
}
fn eat(&self) {
self.as_core().eat()
}
fn sleep(&self) {
self.as_core().sleep()
}
}
fn main() {
let animals = vec![
Animal::Cat(Cat {
name: String::from("Garfield"),
}),
Animal::Lion(Lion { tag_id: 12 }),
];
//later:
for animal in animals {
animal.sleep();
if let Animal::Lion(animal) = animal {
animal.roar()
}
}
}
Which you can see on the playground
Aside: If I had lots of code that was conditional on whether the object was a lion I'd add a helper function like:
impl Animal {
fn as_lion(&self) -> Option<&Lion> {
match(self) {
Animal::Lion(lion) => Some(lion),
_ => None
}
}
}
which I could then use as:
animal.as_lion().map(Lion::roar)
Related
This is the Trait example from the rust by example book
struct Sheep { naked: bool, noise: &'static str }
trait Animal {
// Associated function signature; `Self` refers to the implementor type.
fn new(name: &'static str) -> Self;
// Method signatures; these will return a string.
fn name(&self) -> &'static str;
fn noise(&self) -> &'static str;
// Traits can provide default method definitions.
fn talk(&self) {
println!("{} says {}", self.name(), self.noise());
}
}
impl Sheep {
fn is_naked(&self) -> bool {
self.naked
}
fn shear(&mut self) {
if self.is_naked() {
// Implementor methods can use the implementor's trait methods.
println!("{} is already naked...", self.name());
} else {
println!("{} gets a haircut!", self.name);
self.naked = true;
}
}
}
// Implement the `Animal` trait for `Sheep`.
impl Animal for Sheep {
// `Self` is the implementor type: `Sheep`.
fn new(name: &'static str) -> Sheep {
Sheep { name: name, naked: false }
}
fn name(&self) -> &'static str {
self.name
}
fn noise(&self) -> &'static str {
if self.is_naked() {
"baaaaah?"
} else {
"baaaaah!"
}
}
// Default trait methods can be overridden.
fn talk(&self) {
// For example, we can add some quiet contemplation.
println!("{} pauses briefly... {}", self.name, self.noise());
}
}
fn main() {
// Type annotation is necessary in this case.
let mut dolly: Sheep = Animal::new("Dolly");
// TODO ^ Try removing the type annotations.
dolly.talk();
dolly.shear();
dolly.talk();
}
Let's say I want to add a other animal, for example a cat. A cat can't be naked, but has a name, and makes some noise.
struct Cat { name: &'static str }
impl Animal for Cat {
// `Self` is the implementor type: `Sheep`.
fn new(name: &'static str) -> Cat {
Cat { name: name }
}
fn name(&self) -> &'static str {
self.name
}
fn noise(&self) -> &'static str {
"meow."
}
fn talk(&self) {
println!("{} pauses briefly... {}", self.name, self.noise());
}
}
Here I implemented the exact same name() function twice. That's not a problem for this tiny function, but what if both Sheep and Cat implemented a long and complex function, that was using data from their instance (&self) ? Should I copy/paste the same function in the two impl blocks ?
First I looked to traits, that seems to be a good way to avoid code repetition.
trait Name {
fn name(&self) -> &'static str {
self.name
}
}
impl Name for Sheep {}
impl Name for Cat {}
But that didn't worked, because this trait could be implemented on a struct that doesn't have a name field.
no field 'name' on type '&mut Self'
So my question is: how should I organise my code in order to avoid repetition of method's implementations on different structs, for methods that have access to a mutable reference of their structs?
When you implement a trait, you need to implement everything of it. You're probably looking for some mechanism other languages call abstract classes. We don't have that in Rust (for many reasons). You can always move complex code outside and reuse it.
trait Animal {
fn new(name: &'static str) -> Self;
fn name(&self) -> &'static str;
fn talk(&self);
}
struct Cow {
age: Option<u32>,
name: &'static str,
}
struct Sheep {
name: &'static str,
}
impl Animal for Sheep {
fn new(name: &'static str) -> Self {
Self { name }
}
fn name(&self) -> &'static str {
self.name
}
fn talk(&self) {
complex(self, "baaahh");
}
}
impl Cow {
fn set_age(&mut self, age: u32) {
self.age = Some(age);
}
}
impl Animal for Cow {
fn new(name: &'static str) -> Self {
Self { age: None, name }
}
fn name(&self) -> &'static str {
self.name
}
fn talk(&self) {
if let Some(age) = &self.age {
complex(self, "moooooo");
} else {
complex(self, "MOOOOOOO!!!!");
}
}
}
fn complex<A>(animal: &A, noise: &'static str)
where
A: Animal,
{
println!("Animal {} makes noise {}", animal.name(), noise);
}
fn animal<A>(name: &'static str) -> A
where
A: Animal,
{
A::new(name)
}
fn main() {
let dolly: Sheep = animal("Dolly");
let mut lolly: Cow = animal("Lolly");
dolly.talk();
lolly.talk();
lolly.set_age(5);
lolly.talk();
}
In case of a few structs you should really just duplicate code. If you're planning to implement 50 structs, then maybe create a macro for this
impl AudioNode for Echo {
default_audionode_impl!()
fn process(&mut self) {
// ..
}
}
This question already has answers here:
Simulating field inheritence with composition
(1 answer)
How to avoid code duplication of different structs with semantically equal fields/properties?
(1 answer)
How do I reuse code for similar yet distinct types in Rust?
(2 answers)
Closed 2 years ago.
I'm learning design patterns with GoFPatterns and trying to do their "Traffic Signals" exercise in Rust.
I came up with a solution:
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum SignalState {
STOP,
CAUTION,
GO,
}
trait TrafficSignal {
fn change_state(&mut self) {
match self.get_state() {
SignalState::STOP => self.set_state(SignalState::GO),
SignalState::CAUTION => self.set_state(SignalState::STOP),
SignalState::GO => {
self.passage_granted();
self.set_state(SignalState::CAUTION);
}
}
}
fn request_passage(&mut self) {
self.set_passage_requested(true);
}
fn passage_granted(&mut self) {
self.set_passage_requested(false);
}
fn set_passage_requested(&mut self, new_passage_requested: bool);
fn set_state(&mut self, new_state: SignalState);
fn get_state(&self) -> SignalState;
fn get_passage_requested(&self) -> bool;
fn get_message(&self) -> String;
}
struct TrafficLight {
passage_requested: bool,
state: SignalState,
}
impl TrafficLight {
fn new() -> Self {
Self {
passage_requested: false,
state: SignalState::STOP,
}
}
}
impl TrafficSignal for TrafficLight {
fn set_passage_requested(&mut self, new_passage_requested: bool) {
self.passage_requested = new_passage_requested;
}
fn set_state(&mut self, new_state: SignalState) {
self.state = new_state;
}
fn get_state(&self) -> SignalState {
self.state
}
fn get_passage_requested(&self) -> bool {
self.passage_requested
}
fn get_message(&self) -> String {
format!("Traffic Light : {:?}", self.state)
}
}
struct WalkSign {
passage_requested: bool,
state: SignalState,
}
impl WalkSign {
fn new() -> Self {
Self {
passage_requested: false,
state: SignalState::STOP,
}
}
}
impl TrafficSignal for WalkSign {
fn set_passage_requested(&mut self, new_passage_requested: bool) {
self.passage_requested = new_passage_requested;
}
fn set_state(&mut self, new_state: SignalState) {
self.state = new_state;
}
fn get_state(&self) -> SignalState {
self.state
}
fn get_passage_requested(&self) -> bool {
self.passage_requested
}
fn get_message(&self) -> String {
format!("Walk Sign : {:?}", self.state)
}
}
There is a lot of code duplication because in Rust we can't inherit properties, so I create setters and getters for each property to use in the TrafficSignal trait.
How could I do it better?
As the title says. There seems to be a internal cache in libloading for libaries with the same path. How would I clear it or drop an item from it.
The following mwe.
use libloading::{Library, Symbol};
use std::process::Command;
fn write(i: i64) {
std::fs::write("source.rs", format!("
#[no_mangle]
pub extern \"C\" fn fun() {{
println!(\"{}\");
}}", i)).expect("write error");
}
fn compile() {
Command::new("rustc")
.arg("--crate-type")
.arg("cdylib")
.arg("source.rs")
.arg("-o")
.arg("binary.o")
.output()
.expect("compile error");
}
fn run() {
let lib = Library::new("./binary.o").unwrap();
unsafe {
lib.get::<Symbol<unsafe extern "C" fn()>>(b"fun").unwrap()()
}
}
fn main() {
for i in 0..10 {
write(i);
compile();
run();
}
}
returns
0
0
0
...
I would however like
0
1
2
...
I ended up reinventing the wheel. In case anybody will stumble upon the same issue:
use libc;
use std::ffi::CStr;
use std::mem::transmute;
pub struct SharedLib {
handle: *mut libc::c_void,
}
impl SharedLib {
pub fn new(path: &str) -> Self {
let path = CStr::from_bytes_with_nul(path.as_bytes()).unwrap();
let handle = unsafe { libc::dlopen(path.as_ptr(), libc::RTLD_NOW) };
SharedLib {
handle: handle,
}
}
pub fn run<T>(&self, name: &str) -> T {
if self.handle.is_null() {
panic!("error");
}
let name = CStr::from_bytes_with_nul(name.as_bytes()).unwrap();
unsafe {
let ptr = libc::dlsym(self.handle, name.as_ptr());
transmute::<_, fn() -> T>(ptr)()
}
}
}
impl Drop for SharedLib {
fn drop(&mut self) {
unsafe {
libc::dlclose(self.handle);
}
}
}
The following code doesn't compile, because it says that requests is not defined when I try to use it in the operations module.
I think this maybe has something to do with importing modules maybe, but I'm really new to rust and don't understand. I thought I could just do module::struct and as long as the struct was public I would be able to access it.
Can someone explain why this doesn't work, and how to make this work?
pub mod vehicles {
pub struct Vehicle {
_vehicle_id: u64,
_capacity: u32,
}
impl Vehicle {
pub fn new(id: u64, cap: u32) -> Vehicle {
Vehicle {
_vehicle_id: id,
_capacity: cap,
}
}
pub fn get_id(&self) -> u64 {
self._vehicle_id
}
pub fn get_capacity(&self) -> u32 {
self._capacity
}
}
}
mod requests {
pub struct Request {
_request_id: u64,
_request_time: i64,
_origin: u64,
_destination: u64,
_assigned: bool,
}
impl Request {
pub fn new(id: u64, time: i64, origin: u64, dest: u64) -> Request {
Request {
_request_id: id,
_request_time: time,
_origin: origin,
_destination: dest,
_assigned: false,
}
}
pub fn get_id(&self) -> u64 {
self._request_id
}
pub fn get_request_time(&self) -> i64 {
self._request_time
}
pub fn get_origin(&self) -> u64 {
self._origin
}
pub fn get_destination(&self) -> u64 {
self._destination
}
pub fn is_assigned(&self) -> bool {
self._assigned
}
}
}
pub mod operations {
#[derive(Clone, Copy)]
pub enum OperationType {
PICKUP,
DROPOFF,
}
pub struct Operation {
_request: requests::Request,
_location: u64,
_operation_type: OperationType,
_expected_time: i64,
}
impl Operation {
pub fn new(request: requests::Request, optype: OperationType, location: u64, time: i64) -> Self {
Self {
_request: request,
_operation_type: optype,
_location: location,
_expected_time: time,
}
}
pub fn get_location(&self) -> u64 {
self._location
}
pub fn get_request(&self) -> &requests::Request {
&self._request
}
pub fn get_type(&self) -> OperationType {
self._operation_type
}
pub fn get_expected_time(&self) -> i64 {
self._expected_time
}
}
}
Since Rust 2018, you must use crate keyword, add use crate::requests; somewhere (generally on the top) in the module where you want use requests.
See, the relevant module section in the book.
I have the following method:
internal typealias MaybeError<T> = Either<GenericError, T>
override fun createCompany(companyDomain: CompanyDomain): MaybeError<CompanyDomain> =
checkCompany(companyDomain).map { it.toEntity() }.fold({ Either.left(it) }) { company ->
with (companyRepository) {
isCompanyExists(company).fold({ Either.left(it) }) { isExists ->
if (isExists) return#with Either.left(CompanyNameExists(companyDomain))
createCompany(company).fold({ Either.right(companyDomain) }) { Either.left(it) }
}
}
}
Is there a better/more idiomatic way to write this using Arrow?
It is hard to refactor because I can only assume what used methods should return. But I guess the methods returns MaybeError. In this case we can omit fold({ Either.left(it) }) and we can use map or flatMap.
internal typealias MaybeError<T> = Either<GenericError, T>
override fun createCompany(companyDomain: CompanyDomain): MaybeError<CompanyDomain> =
checkCompany(companyDomain)
.map { it.toEntity() }
.flatMap { company ->
companyRepository.isCompanyExists(company)
.flatMap { isExists ->
if (isExists) {
MaybeError.left(CompanyNameExists(companyDomain))
} else {
companyRepository.createCompany(company)
}
}
}