Objects and classes in Rust - oop

I am fiddling around with Rust, going by the examples, trying to make a class. I have been looking at the example of StatusLineText
It keeps raising the errors:
error: `self` is not available in a static method. Maybe a `self` argument is missing? [E0424]
self.id + self.extra
^~~~
error: no method named `get_total` found for type `main::Thing` in the current scope
println!("the thing's total is {}", my_thing.get_total());
^~~~~~~~~
My code is rather simple:
fn main() {
struct Thing {
id: i8,
extra: i8,
}
impl Thing {
pub fn new() -> Thing {
Thing { id: 3, extra: 2 }
}
pub fn get_total() -> i8 {
self.id + self.extra
}
}
let my_thing = Thing::new();
println!("the thing's total is {}", my_thing.get_total());
}

You need to add an explicit self parameter to make methods:
fn get_total(&self) -> i8 {
self.id + self.extra
}
Functions without the explicit self parameter are considered associated functions, which can be called without a specific instance.

Related

What's the idiomatic way of creating a Rust Error shim?

I have two error types that are defined in external crates. Let's call them foo::Error and bar::Error. Because they are both defined externally, I cannot create a direct From or Into conversion between the two.
In my own crate, I'm using implementing a trait from bar, which expects return values of Result<T, bar::Error>. In the methods of my implementation, I'm calling functions from foo, which return many Result<T, foo::Error> types.
I would like to map these foo::Error errors to bar::Error errors, so that my trait implementation is neat, but the best that I can do is create a separate shim error, and then have a clunky .map_err(ShimErr::from)? on everything. This can start to litter up my code if I'm calling many foo:: functions in my implementation.
use foo;
use bar;
struct ShimErr(foo::Error);
impl From<foo::Error> for ShimErr {
fn from(e: foo::Error) -> Self {
Self(e)
}
}
impl From<ShimErr> for bar::Error {
fn from(e: ShimErr) -> Self {
Self{}
}
}
struct MyTraitImpl {}
impl bar::SomeTrait for MyTraitImpl {
fn do_something() -> Result<i32, bar::Error> {
// FIXME: THIS IS CLUNKY
let foo_val: i32 = foo::call_something().map_err(ShimErr::from)?;
Ok(foo_val * 2)
}
}
Is there a better way? In an ideal world, there would be a way to just use ?
If I remove map_err and try to use ? directly:
error[E0277]: `?` couldn't convert the error to `bar::Error`
--> shimtest.rs:32:49
|
32 | let foo_val: i32 = foo::call_something()?;
| ^ the trait `std::convert::From<foo::Error>` is not implemented for `bar::Error`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= help: the following implementations were found:
<bar::Error as std::convert::From<ShimErr>>
= note: required by `std::convert::From::from`
The way I handle multiple different errors from other crates would be to make my own Error enum and use that instead of other errors directly.
So instead of a tuple struct that only contains foo::Error, you can create an enum that can have both foo::Error and bar::Error. After this, you can convert all the errors to the custom enum error with ?.
In certain cases, I think you'll still need to use something like .map_err(Into::into) occasionally, but usually only when the variable being returned is a Result with an external error.
use bar::Error as BarError;
use foo::Error as FooError;
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::result::Result as StdResult;
/// Common result type that uses the custom error
pub type Result<T> = StdResult<T, Error>;
/// Common error type used as a holder for errors from various other libraries.
#[derive(Debug)]
pub enum Error {
Bar(BarError),
Foo(FooError),
}
impl From<BarError> for Error {
fn from(err: BarError) -> Error {
Error::Bar(err)
}
}
impl From<FooError> for Error {
fn from(err: FooError) -> Error {
Error::Foo(err)
}
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
match *self {
Error::Bar(ref inner) => inner.fmt(f),
Error::Foo(ref inner) => inner.fmt(f),
}
}
}
impl StdError for Error {
fn description(&self) -> &str {
match *self {
Error::Bar(ref inner) => inner.description(),
Error::Foo(ref inner) => inner.description(),
}
}
}
Now you can use the custom Result and Error as follows.
// this Result is now our custom Result type with custom error
// same as std::result::Result<i32, Error> (with Error as our custom error)
fn do_something() -> Result<i32> {
let foo_val: i32 = foo::call_something()?; // should convert to our custom error
Ok(foo_val * 2)
}
fn do_something_else() -> Result<i32> {
// directly returning a Result<> with a different error type will need map_err
foo::call_something().map_err(Into::into)
}

What is the most idiomatic way to merge two error types?

I have a type Foo whose methods may "raise" errors of an associated type Foo::Err.
pub trait Foo {
type Err;
fn foo(&mut self) -> Result<(), Self::Err>;
}
I have another trait Bar with a method intended to process a Foo. Bar may issue errors of its own (specified by an associated type Bar::Err), but it may also encounter errors generated by the Foo it is processing.
I can see two ways to do this, but I don't know which one would be the most idiomatic to Rust.
The first one embeds a result in a result:
pub trait Bar1 {
type Err;
fn bar<F: Foo>(&mut self, foo: F) -> Result<Result<F, F::Err>, Self::Err>;
}
The second one merges the two error types into a dedicated enum:
pub trait Bar2 {
type Err;
fn bar<F: Foo>(&mut self, foo: F) -> Result<F, Choice<F::Err, Self::Err>>;
}
The second one looks semantically cleaner, but creates some hurdles for handling the additional enum.
playground
Typically you don't do a "merge", but instead use nested errors, like this.
enum IntError {
Overflow,
Underflow
}
enum StrError {
TooLong,
TooShort,
}
enum GenericError {
Int(IntError),
Str(StrError),
}
impl From<IntError> for GenericError {
fn from(e: IntError) -> Self {
GenericError::Int(e)
}
}
impl From<StrError> for GenericError {
fn from(e: StrError) -> Self {
GenericError::Str(e)
}
}
You should use a trait object Error, and you return the first error that you encounter:
pub trait Bar {
fn bar<F: Foo>(&mut self, foo: F) -> Result<F, Box<dyn Error>>;
}
or implement your trait like this:
impl Bar for MyType {
type Err = Box<dyn Error>;
fn bar<F: Foo>(&mut self, foo: F) -> Result<F, Self::Err>;
}
If you really want to have your two errors (but this is strange because one error suffices to make the process not ok), you can use a crate like failure to create an "error trace".
As a general advice, you should not forget to use the traits from std to add more semantic to your code.

Wrong number of lifetime parameters when using a modified `Chars` iterator

I want to implement the IntoIterator trait for a struct containing a String. The iterator is based on the chars() iterator, is supposed to count the '1' chars and accumulate the result. This is a simplified version of what I got so far:
use std::iter::Map;
use std::str::Chars;
fn main() {
let str_struct = StringStruct { system_string: String::from("1101") };
for a in str_struct {
println!("{}", a);
}
}
struct StringStruct {
system_string: String
}
impl IntoIterator for StringStruct {
type Item = u32;
type IntoIter = Map<Chars, Fn(char) -> u32>;
fn into_iter(self) -> Self::IntoIter {
let count = 0;
return self.system_string.chars().map(|c| match c {
Some('1') => {
count += 1;
return Some(count);
},
Some(chr) => return Some(count),
None => return None
});
}
}
Expected output: 1, 2, 2, 3
This fails with:
error[E0107]: wrong number of lifetime parameters: expected 1, found 0
--> src/main.rs:17:25
|
17 | type IntoIter = Map<Chars, Fn(char) -> u32>;
| ^^^^^ expected 1 lifetime parameter
The chars iterator should have the same lifetime as the StringStruct::system_string, but I have no idea how to express this or if this approach is viable at all.
To answer the question you asked, I'd recommend to impl IntoIterator for &StringStruct (a reference to a StringStruct instead of the struct directly). The code would look like this:
impl<'a> IntoIterator for &'a StringStruct {
type Item = u32;
type IntoIter = Map<Chars<'a>, Fn(char) -> u32>;
// ...
}
However, you will notice many more errors that have a different origin afterwards. The next error that pops up is that Fn(char) -> u32 does not have a constant size at compile time.
The problem is that you try to name the type of your closure by writing Fn(char) -> u32. But this is not the type of your closure, but merely a trait which is implemented by the closure. The type of a closure can't be named (sometimes called "Voldemort type").
This means that, right now, you can't specify the type of a Map<_, _> object. This is a known issue; the recently accepted impl Trait-RFC might offer a workaround for cases like this. But right now, it's not possible, sorry.
So how to solve it then? You need to create your own type that implements Iterator and use it instead of Map<_, _>. Note that you can still use the Chars iterator. Here is the full solution:
struct StringStructIter<'a> {
chars: Chars<'a>,
count: u32,
}
impl<'a> Iterator for StringStructIter<'a> {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
self.chars.next().map(|c| {
if c == '1' {
self.count += 1;
}
self.count
})
}
}
impl<'a> IntoIterator for &'a StringStruct {
type Item = u32;
type IntoIter = StringStructIter<'a>;
fn into_iter(self) -> Self::IntoIter {
StringStructIter {
chars: self.system_string.chars(),
count: 0,
}
}
}
fn main() {
let str_struct = StringStruct { system_string: String::from("1101") };
for a in &str_struct {
println!("{}", a);
}
}
And just a small note: an explicit return when not necessary is considered bad style in Rust. Better stick to rule and write idiomatic code by removing return whenever possible ;-)

Rust: Create an Iterator out of Default and Succ?

I have the following code in a repo:
impl<Id> IdAllocator<Id> where
Id : Clone + Default + Add<u32, Id>,
{
pub fn new() -> IdAllocator<Id> {
IdAllocator {
next: Default::default()
}
}
// Produce an Id that hasn't been produced yet by this object.
pub fn allocate(&mut self) -> Id {
let ret = self.next.clone();
self.next = self.next + 1;
ret
}
}
But it seems a little clumsy, especially since the Add instance is only used as a succ function (generating the next value in sequence). Is there some Succ class I can use? And if so, is there already some Iterator construction somewhere in the standard library that already does this Default+Succ pattern?
Thanks!
No, unfortunately, there is no Succ-like thing in the standard library. The closest thing you can find is range() family of iterators, however, it uses Add and One numeric traits to generate items. You can do it this way (the idea is basically the same as yours, but this version is slightly more generic due to One trait usage):
use std::num::One;
use std::default::Default;
struct IdAllocator<T> {
current: T
}
impl<T: Default> IdAllocator<T> {
#[inline]
pub fn new() -> IdAllocator<T> {
IdAllocator {
current: Default::default()
}
}
}
impl<T: Add<T, T>+One+Clone> Iterator<T> for IdAllocator<T> {
fn next(&mut self) -> Option<T> {
let next = self.current + One::one();
self.current = next.clone();
Some(next)
}
}
fn main() {
let a = IdAllocator::<uint>::new();
for i in a.take(10) {
println!("{}", i);
}
}
(try it here)

Traits and implementations in Rust

I have the following types:
trait Monster {
fn attack(&self);
fn new(int) -> Self;
}
struct CookiesMonster {
cookies: int,
hungry_level: int,
}
impl Monster for CookiesMonster {
fn new(i: int) -> CookiesMonster {
CookiesMonster { cookies: i, hungry_level: i + 1 }
}
fn attack(&self) {
println!("I have {:d} cookies!!", self.cookies)
}
}
struct Dummy {
count: int
}
impl Dummy {
fn new(i: int) -> Dummy {
Dummy { count: i }
}
}
Now, this works:
let monster: CookiesMonster = Monster::new(10);
let dummy = Dummy::new(10);
But this doesn't:
let monster = CookiesMonster::new(10);
Why can't I call the new method directly on the CookiesMonster type?
Because that's how traits work at the moment. Static methods in traits must be called on the trait, and not on the implementor of the trait.
Note that calling the methods on the trait instead of on the type implementing the trait allows cases like this to be unambiguous: Consider if you added the following code to your example:
trait Newable {
fn new(int) -> Self;
}
impl Newable for CookiesMonster {
fn new(i: int) -> CookiesMonster {
CookiesMonster { cookies: i, hungry_level: 0 }
}
}
In this context, Monster::new still works, but CookiesMonster::new would be ambiguous.
(In this example, it figures how which implementation of the trait to use based on type inference. A generalized syntax such as Trait::<for Type>::static_method has been discussed as a way to explicitly write down one's intentions, but I am not sure how far along that is.)
Update circa 15 July 2014: the "Unified Function Call Syntax" proposal tracks the work alluded to in the previous paragraph. See Rust RFC PR 132. My understanding is that UFCS as described in that RFC would actually allow for you to write CookiesMonster::new when Monster is the only trait in scope that both (1.) provides the new method and (2.) is unambiguously implemented for CookiesMonster.