I'm trying to recreate the way error handling is done in std::io. I'm basing my code on this article.
My problem is that I've put my code in a separate mod and now I can't see the enum value I want to return. Code sample:
mod error {
use std::str::SendStr;
pub type ProgramResult<T> = Result<T, ProgramError>;
#[deriving(Show)]
pub struct ProgramError {
kind: ProgramErrorKind,
message: SendStr
}
/// The kinds of errors that can happen in our program.
/// We'll be able to pattern match against these.
#[deriving(Show)]
pub enum ProgramErrorKind {
Configuration
}
impl ProgramError {
pub fn new<T: IntoMaybeOwned<'static>>(msg: T, kind: ProgramErrorKind) -> ProgramError {
ProgramError {
kind: kind,
message: msg.into_maybe_owned()
}
}
}
}
I cannot see Configuration anywhere else in my code, even though the enum is public and duly imported in all other mods that try to use this. Any ideas?
Are you using the module resolution operator when attempting to reference the enum types? For instance, here's an example that works fine:
mod error {
#[deriving(Show)]
pub enum ProgramErrorKind {
Configuration,
SomethingElse
}
}
fn main() {
// You can import them locally with 'use'
use error::Configuration;
let err = Configuration;
// Alternatively, use '::' directly
let res = match err {
error::Configuration => "Config error!",
error::SomethingElse => "I have no idea.",
};
println!("Error type: {}", res);
}
Related
I have made a minimal example. In lib.rs:
mod sealed {
pub enum Choice {
A,
B,
}
}
pub fn print_choice(choice: sealed::Choice) {
match choice {
sealed::Choice::A => println!("Choice A"),
sealed::Choice::B => println!("Choice B"),
}
}
I think: The enum Choice is public. However, it's in a private mod, and cannot be reached from outside of the crate. Therefore the function print_choice is not callable at all.
What is wrong with my thinking?
What is wrong with my thinking?
You could have something like
pub use sealed::Choice;
at the toplevel. That is a common way to split up the implementation while providing a simple single-module interface to external users.
Or even just an other function returning an instance of Choice. Since it's pub, it's not considered a private type.
If you change pub enum to pub(crate) enum (meaning you state that the enum can not be made visible outside the crate) then the compilation will fail.
An important thing to understand is that Choice is not private. It is inside a private module, and thus unnamable, but it is public.
The only thing the module's privacy affects is that you cannot access the enum via this path. You can do any other thing with it, e.g. accessing it via other path it is reexported into:
mod sealed {
pub enum Choice {
A,
B,
}
}
pub use sealed::Choice;
// In other module
crate::Choice::A;
Or manipulate it with generics and traits, for example:
mod sealed {
pub enum Choice {
A,
B,
}
impl Default for Choice {
fn default() -> Self { Self::A }
}
}
pub fn print_choice(choice: sealed::Choice) { ... }
// In other module
crate::print_choice(Default::default());
mod sealed {
#[derive(Debug)]
pub enum Choice {
A,
B,
}
}
pub fn print_choice(choice: sealed::Choice) { crate::print(choice) }
// In other module
pub fn print<T: Debug>(v: T) { ... }
This private type seems to leak, but you don't have full control over it.
You cannot build such a private Choice, but another public function can provide you with it.
mod outer {
mod sealed {
pub enum Choice {
A,
B,
}
}
pub fn print_choice(choice: sealed::Choice) {
match choice {
sealed::Choice::A => println!("Choice A"),
sealed::Choice::B => println!("Choice B"),
}
}
pub fn make_choice(n: u32) -> sealed::Choice {
if n % 2 == 0 {
sealed::Choice::A
} else {
sealed::Choice::B
}
}
}
fn main() {
// let ch = outer::sealed::Choice::A; // error: module `sealed` is private
let ch = outer::make_choice(2);
outer::print_choice(ch);
}
I am trying to understand following enum from this repo
#[repr(C)]
#[derive(BorshSerialize, BorshDeserialize, Debug, Clone)]
pub struct InitEscrowArgs {
pub data: EscrowReceive,
}
#[repr(C)]
#[derive(BorshSerialize, BorshDeserialize, Debug, Clone)]
pub struct ExchangeArgs {
pub data: EscrowReceive,
}
#[derive(BorshSerialize, BorshDeserialize, Clone)]
pub enum EscrowInstruction {
InitEscrow(InitEscrowArgs),
Exchange(ExchangeArgs),
CancelEscrow(),
}
and it's use of it in this match from this repo.
pub fn process(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult {
let instruction = EscrowInstruction::try_from_slice(instruction_data)?;
match instruction {
EscrowInstruction::InitEscrow(args) => {
msg!("Instruction: Init Escrow");
Self::process_init_escrow(program_id, accounts, args.data.amount)
}
EscrowInstruction::Exchange(args) => {
msg!("Instruction: Exchange Escrow");
Self::process_exchange(program_id, accounts, args.data.amount)
}
EscrowInstruction::CancelEscrow() => {
msg!("Instruction: Cancel Escrow");
Self::process_cancel(program_id, accounts)
}
}
}
I understand that this try_from_slice method gets some sort of byte array and deserialize it.
I do not understand how it determines which enum value to use.
The enum has 3 choices, InitEscrow / Exchange / CancelEscrow, but what determines the match to know which one it is suppose to select?
Seem to me the InitEscrowArgs and ExchangeArgs both takes in same struct. Both containing data that is EscrowReceive data type.
Method try_from_slice is part of the BorshDeserialize trait, which is derived on the enum in question. So, the choice between enum variants is made by the implementation of deserializer.
To see what is really going on, I've built the simplest possible example:
use borsh::BorshDeserialize;
#[derive(BorshDeserialize)]
enum Enum {
Variant1(u8),
Variant2,
}
By using cargo expand and a little manual cleanup, we can get the following equivalent code:
impl borsh::de::BorshDeserialize for Enum {
fn deserialize(buf: &mut &[u8]) -> Result<Self, std::io::Error> {
let variant_idx: u8 = borsh::BorshDeserialize::deserialize(buf)?;
let return_value = match variant_idx {
0u8 => Enum::Variant1(borsh::BorshDeserialize::deserialize(buf)?),
1u8 => Enum::Variant2,
_ => {
let msg = format!("Unexpected variant index: {}", variant_idx);
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
msg,
));
}
};
Ok(return_value)
}
}
Where the inner deserialize calls refers to impl BorshDeserialize for u8:
fn deserialize(buf: &mut &[u8]) -> Result<Self> {
if buf.is_empty() {
return Err(Error::new(
ErrorKind::InvalidInput,
ERROR_UNEXPECTED_LENGTH_OF_INPUT,
));
}
let res = buf[0];
*buf = &buf[1..];
Ok(res)
}
So, it works the following way:
Deserializer tries to pull one byte from input; if there's none - this is an error.
This byte is interpreted as an index of enum variant; if it doesn't match to one of variants - this is an error.
If the variant contains any data, deserializer tries to pull this data from the input; if it fails (according to the inner type's implementation) - this is an error.
I have an enum with a String:
enum MyLovelyEnum {
Thing(String),
}
For tests, I would like to be able to pass in a &'static str to avoid MyLovelyEnum::Thing("abc".to_string) over and over.
I found that you can do this nicely with structs with a constructor:
// From: https://hermanradtke.com/2015/05/06/creating-a-rust-function-that-accepts-string-or-str.html
struct Person {
name: String,
}
impl Person {
fn new<S: Into<String>>(name: S) -> Person {
Person { name: name.into() }
}
}
fn main() {
let person = Person::new("Herman");
let person = Person::new("Herman".to_string());
}
I know I can use lifetimes or Cow as described in What's the best practice for str/String values in Rust enums? or I can make my own function.
Is there something close to the example in the blog post for enums? e.g.
// this is the kind of thing I am after but this specifically is not correct syntax
enum MyLovelyEnum {
Thing<S: Into<String>>(S)
}
You can create a generic enum:
enum MyLovelyEnum<S>
where
S: Into<String>,
{
Thing(S),
}
MyLovelyEnum::Thing("a");
MyLovelyEnum::Thing("b".to_string());
I likely wouldn't do that in my code, instead opting to create a constructor, much like the blog post you linked:
enum MyLovelyEnum {
Thing(String),
}
impl MyLovelyEnum {
fn thing(s: impl Into<String>) -> Self {
MyLovelyEnum::Thing(s.into())
}
}
MyLovelyEnum::thing("a");
MyLovelyEnum::thing("b".to_string());
I am trying to implement a function-like macro in Rust. It should be used as follows:
service!(FooService, "/foo_service");
This macro call shall generate a struct FooServiceClient and an impl FooServiceClient with various methods.
I am using the syn + quote duo and here are some relevant bits of my code:
struct ServiceDefinition {
pub service_name: String,
pub scope: String,
pub operations: Vec<Operation>,
}
impl Parse for ServiceDefinition {
fn parse(input: ParseStream) -> Result<Self> {
let params = Punctuated::<Expr, Token![,]>::parse_terminated(&input)?;
let service_name = {
let expr = ¶ms[0];
match expr {
Expr::Path(ref expr_path) => {
let leading_colon = expr_path.path.leading_colon;
if let Some(leading_colon) = leading_colon {
return Err(Error::new(expr_path.span(), "expected unscoped identifier"));
}
if expr_path.path.segments.len() != 1 {
return Err(Error::new(expr_path.span(), "expected unscoped identifier"));
}
expr_path.path.segments.first().unwrap().ident
},
_ => {
return Err(Error::new(expr.span(), "expected service name"));
}
}
};
Ok(ServiceDefinition {
service_name: service_name.to_string(),
scope: "/foo".to_string(),
operations: vec![],
})
}
}
As you can see, this is still at the early stages of work in progress, but when I try to compile it, this is the error I'm getting:
error[E0599]: no method named `span` found for reference `&ExprPath` in the current scope
--> service_commons/src/lib.rs:58:57
|
58 | return Err(Error::new(expr_path.span(), "expected unscoped identifier"));
| ^^^^ method not found in `&ExprPath`
|
As far as I can see in the docs, the method span should be on these types, but it's obviously missing for some reason.
I am using syn = {version = "1.0.70", features = ["full"]} in my Cargo file.
What am I missing here?
span comes from the Spanned trait, so it needs to be in scope:
use syn::spanned::Spanned;
See also:
Why do I need to import a trait to use the methods it defines for a type?
I'm working with a library that uses Rust types to keep track of state. As a simplified example, say you have two structs:
struct FirstStruct {}
struct SecondStruct {}
impl FirstStruct {
pub fn new() -> FirstStruct {
FirstStruct {}
}
pub fn second(self) -> SecondStruct {
SecondStruct {}
}
// configuration methods defined in this struct
}
impl SecondStruct {
pub fn print_something(&self) {
println!("something");
}
pub fn first(self) -> FirstStruct {
FirstStruct {}
}
}
And to actually use these structs you usually follow a pattern like so, after printing you may stay in second state or go back to first state depending on how you're using the library:
fn main() {
let first = FirstStruct::new();
let second = first.second(); // consumes first
second.print_something();
// go back to default state
let _first = second.first();
}
I want to create my own struct that handles the state changes internally and simplifies the interface. This also lets me have a single mutable reference around that I can pass to other functions and call the print method. Using it should look something like this:
fn main() {
let mut combined = CombinedStruct::new(FirstStruct::new());
combined.print();
}
I've come up with the following solution that works, at least in this simplified example:
enum StructState {
First(FirstStruct),
Second(SecondStruct),
}
struct CombinedStruct {
state: Option<StructState>,
}
impl CombinedStruct {
pub fn new(first: FirstStruct) -> CombinedStruct {
CombinedStruct {
state: Some(StructState::First(first)),
}
}
pub fn print(&mut self) {
let s = match self.state.take() {
Some(s) => match s {
StructState::First(first) => first.second(),
StructState::Second(second) => second,
},
None => panic!(),
};
s.print_something();
// If I forget to do this, then I lose access to my struct
// and next call will panic
self.state = Some(StructState::First(s.first()));
}
}
I'm still pretty new to Rust but this doesn't look right to me. I'm not sure if there's a concept I'm missing that could simplify this or if this solution could lead to ownership problems as my application gets more complicated. Is there a better way to do this?
Playground link
I once had a similar problem and went basically with your solution, but I avoided the Option.
I.e. I basically kept your
enum StructState {
First(FirstStruct),
Second(SecondStruct),
}
If an operation tries to convert a FirstStruct to a SecondStruct, I introduced a function try_to_second roughly as follows:
impl StructState {
fn try_to_second(self) -> Result<SecondState, StructState> {
/// implementation
}
}
In this case, an Err indicates that the StructState has not been converted to SecondStruct and preserves the status quo, while an Ok value indicates successfull conversion.
As an alternative, you could try to define try_to_second on FirstStruct:
impl FirstStruct {
fn try_to_second(self) -> Result<FirstStruct, SecondStruct> {
/// implementation
}
}
Again, Err/Ok denote failure/success, but in this case, you have more concrete information encoded in the type.