How to avoid code repetition in rust (in struct, and traits)? - oop

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) {
// ..
}
}

Related

How to "inherit" properties in an OOP style in Rust? [duplicate]

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?

How do I solve this inheritance problem in rust?

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)

How to clear internal cache of libloading in Rust

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);
}
}
}

Using a struct from another module in a new module

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.

How to use lifetimes for nesting mutable access?

I'm writing code in Rust for parsing streams, trait Stream. The streams can consist of other streams. The trait StreamIterator gives access to the substreams. This is when parsing tar files, zip files and other files that contain files.
While writing this code, I've been unsuccessfully fighting the borrow checker.
The code below is a simplified example. In main a file is opened as a stream. That stream is passed to the analyze function which tries to open the stream as a TarStreamIterator to iterate of the streams in the tar. Each embedded stream is also analyzed.
I think that I might to introduce a second lifetime in the StreamIterator trait.
use std::io;
trait Stream<T> {
fn read(&mut self) -> io::Result<&[T]>;
}
trait StreamIterator<'a,T,S: Stream<T>> {
fn next(&'a mut self) -> Option<io::Result<S>>;
}
struct FileStream {
}
impl<T> Stream<T> for FileStream {
fn read(&mut self) -> io::Result<&[T]> {
Err(io::Error::new(io::ErrorKind::UnexpectedEof, ""))
}
}
struct TarStream<'a> {
stream: &'a mut Stream<u8>
}
impl<'a> Stream<u8> for TarStream<'a> {
fn read(&mut self) -> io::Result<&[u8]> {
self.stream.read()
}
}
struct TarStreamIterator<'a> {
stream: &'a mut Stream<u8>
}
impl<'a> StreamIterator<'a,u8,TarStream<'a>> for TarStreamIterator<'a> {
fn next(&'a mut self) -> Option<io::Result<TarStream>> {
// todo: read tar header
Some(Ok(TarStream{stream: self.stream}))
}
}
fn analyzeAsTar(s: &mut Stream<u8>) -> bool {
let mut tar = TarStreamIterator{stream: s};
while let Some(Ok(mut substream)) = tar.next() {
analyze(&mut substream);
}
true
}
fn analyze(s: &mut Stream<u8>) -> bool {
analyzeAsTar(s)
}
fn main() {
let mut fs = FileStream{};
analyze(&mut fs);
}
This gives this error:
<anon>:38:41: 38:44 error: cannot borrow `tar` as mutable more than once at a time [E0499]
<anon>:38 while let Some(Ok(mut substream)) = tar.next() {
^~~
<anon>:38:41: 38:44 help: see the detailed explanation for E0499
<anon>:38:41: 38:44 note: previous borrow of `tar` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `tar` until the borrow ends
<anon>:38 while let Some(Ok(mut substream)) = tar.next() {
^~~
<anon>:42:2: 42:2 note: previous borrow ends here
<anon>:36 fn analyzeAsTar(s: &mut Stream<u8>) -> bool {
...
<anon>:42 }
There is a workaround. Instead of having a trait with a next() function, one can use a trait with an iterate function. In the example below, TarStreamIterator has a function iterate can accept a closure. (Alternatively, iterator could be called for_each.)
The implementation still has a next function, but the borrow checker accepts this form.
This short example does not actually do anything with the streams.
use std::io;
// generic version of std::io::Read
trait Stream<T> {
fn read(&mut self) -> io::Result<&[T]>;
}
trait StreamIterator<T> {
// call `f` on each of the streams in the iterator
fn iterate<F>(&mut self, mut f: F) where F: FnMut(&mut Stream<T>);
}
struct FileStream {
}
impl<T> Stream<T> for FileStream {
fn read(&mut self) -> io::Result<&[T]> {
Err(io::Error::new(io::ErrorKind::UnexpectedEof, ""))
}
}
struct TarStream<'a> {
stream: &'a mut Stream<u8>
}
impl<'a> Stream<u8> for TarStream<'a> {
fn read(&mut self) -> io::Result<&[u8]> {
self.stream.read()
}
}
struct TarStreamIterator<'a> {
stream: &'a mut Stream<u8>
}
impl<'a> TarStreamIterator<'a> {
// pass the next embedded stream or None if there are no more
fn next(&mut self) -> Option<TarStream> {
Some(TarStream{stream: self.stream})
}
}
impl<'a> StreamIterator<u8> for TarStreamIterator<'a> {
fn iterate<F>(&mut self, mut f: F) where F: FnMut(&mut Stream<u8>) {
while let Some(mut substream) = self.next() {
f(&mut substream);
}
}
}
fn analyze_as_tar(stream: &mut Stream<u8>) {
TarStreamIterator{stream: stream}.iterate(|substream| {
analyze(substream);
});
}
fn analyze(s: &mut Stream<u8>) {
analyze_as_tar(s)
}
fn main() {
let mut fs = FileStream{};
analyze(&mut fs);
}