-
Background: I've been writing mostly Scala for the last few years, mostly in FP style. I also have experience writing Java, Ruby, Python, bash and a few other languages.
-
Caveat: anything below which sounds like a grumble about the language is probably due to ignorance on my part. Please advise/correct me in the comments.
-
For a more eloquent writeup of a similar topic by somebody who can actually write Rust, see Lloyd's excellent blogpost
-
Tools like rustup and cargo are a breath of fresh air after working with sbt for so long!
-
clap is one of the best command line parsing libraries I've used in any language.
-
I got tripped up by the borrowing semantics a couple of times but I managed to
clone()
my way out of trouble. I understand that this is cheating. Need to learn more about borrowing and lifecycles. -
Structs needing to be a fixed size was confusing at first, but you just need to stick dynamic-sized things in boxes to get around it. I wonder why it's ok for the final field to have a dynamic size?
-
Semicolons are annoying. The semantics of a function change depending on the absence or presence of a semicolon on the final line. Include a semicolon -> return
()
. Luckily the compiler gives you a useful warning or error. -
If you take the time to read the compiler errors properly, they are really helpful.
-
Getting compiler warnings for code style (e.g. using camel case instead of snake case) and dead code is quite nice.
-
The
?
syntax for chainingResult
s monadically is nice. But I want to learn more about how it's implemented. I hope it's not hardcoded to work with only theResult
type. Also having to wrap the final result you want to yield inOk()
is a bit clunky. I wonder if you could build a Haskell/Scala-style for comprehension with a macro? -
The automagic conversion using the
From
trait is a bit troubling. Feels quite magical, and works inconsistently.e.g. if I write
struct AWSError { message: String } impl <A: Error> From<A> for AWSError { fn from(err: A) -> AWSError { let message = String::from(err.description()); AWSError { message } } } fn does_table_exist(&self) -> Result<bool, AWSError> { ... let output = self.dynamo_client.describe_table(&describe_table_input)?; // returns Result<..., DescribeTableError> Ok(output.table.is_some()) }
then the implicit conversion from DescribeTableError to AWSError works fine.
But if I write
fn does_table_exist(&self) -> Result<bool, AWSError> { ... self.dynamo_client .describe_table(&describe_table_input) .map(|output| output.table.is_some()) }
then the implicit conversion doesn't kick in and it fails to compile.
-
When chaining operations that return different error types, what should my return type be?
fn new(profile: Option<String>, region: String, table_name: String) -> Result<AWS, ???> { let provider = AWS::build_creds_provider(profile)?; // returns Result<..., CredentialsError> let reg = Region::from_str(region.as_str())?; // returns Result<..., ParseRegionError> let tls_client = default_tls_client()?; // returns Result<..., TlsError> ... }
The error types don't match, but they do all implement the
Error
trait. I made my own error structAWSError
and defined a conversion fromA: Error
toAWSError
. It works, but it feels like there must be an easier way. -
Why doesn't Option have a
foreach
method for performing a side-effect? Apparently this is the most idiomatic way to write it:if let Some(p) = profile { profile_provider.set_profile(p); }
-
The naming conventions in Rust are a bit different from functional languages, so I have to keep looking up method names. e.g.
Option
has a map method calledmap
but a flatmap/bind method calledand_then
. -
Why can't child modules hide anything from their parent? I want a private modifier.
-
I understand why there are two ways of representing a string, but it's still annoying. It seems like whenever I have a string and I need to pass it to a function, it's the wrong type and I have to convert it.
-
After a few evenings of writing Rust, I've got past the frustrating "I know what I want to write but I can't get the syntax right" stage, and I'm starting to be productive. The next step will be to take the code I've written so far, which I know is ugly and verbose, and start looking at ways to refactor it.
-
I haven't written any tests yet. I don't think you can claim to be proficient at a language until you can write tests in it.
-
I've had a quick look at setting up Travis CI to build on multiple platforms. Looks like some kind soul has already done all the hard work and made a template to follow.
Nice ! You covered a bunch of stuff I forgot to mention in my blogpost :)
Re:
?
, my understanding is that it may have been hardcoded, but in the future, will be available for anything that implements theTry
trait, since the extend ? to work over other types PR has been merged. See this RFC for more details.Just copying from the RFC:
?
isResult
isOption
isI've seen one or two crates out there that port do/for too.