I have a function that I would like to reuse and have it accept a parameter of a Decocable struct. For example, this is a simplification of my current code (assume "MyDecodableStruct" is a Decodable struct declared elsewhere in the app):
static func getResults(url: String, parameters: Parameters) {
// On success REST response
if response.result.isSuccess {
struct Results: Decodable {
let items: [MyDecodableStruct]
}
if let jsonResults = try? JSONDecoder().decode(Results.self, from: response.data!) {
//success
}
}
and instead of saying "MyDecodableStruct", I would like it to be any Decodable struct I pass in as a parameter. Something like this:
static func getResults(url: String, parameters: Parameters, myStruct: Decodable) {
// On success REST response
if response.result.isSuccess {
struct Results: Decodable {
let items: [myStruct]
}
if let jsonResults = try? JSONDecoder().decode(Results.self, from: response.data!) {
//success
}
}
and I would call it like
getResults(url: "url", parameters: nil, myStruct: MyDecodableStruct)
I cannot figure out the syntax on how to get this to work though. The errors I get are
Type 'Results' does not conform to protocol 'Decodable'
Expected element type
Any ideas on the best way to handle this?
When you want to pass a type as a parameter, you need to declare the type of the parameter as metatype. In your case, it's a generic type which needs to conform to Decodable.
So you may need to write something like this:
struct Results<Element: Decodable>: Decodable {
let items: [Element]
}
static func getResults<Element: Decodable>(url: String, parameters: Parameters?, myStruct: Element.Type) {
//...
// On success REST response
if response.result.isSuccess {
do {
let jsonResults = try JSONDecoder().decode(Results<Element>.self, from: response.data!)
//success
print(jsonResults)
} catch {
//Better not dispose errors silently
print(error)
}
}
//...
}
Swift says types cannot be nested in generic context, so I moved it out to the outer non-generic context.
Call it as:
getResults(url: "url", parameters: nil, myStruct: MyDecodableStruct.self)
Related
This question already has answers here:
How to restrict the construction of struct?
(2 answers)
Closed 5 months ago.
I want to give some business-rule guarantees about certain structs. For example, that an EmailAddress is a valid email, or that a DateRange has a from that lies before a from, and so on. So that when passing such a value around, it is guaranteed to adhere to all business rules for that struct.
struct InvalidEmailAddress;
struct EmailAddress {
value: String
}
impl EmailAddress {
fn new(value: String) -> Result<Self, InvalidEmailAddress> {
if value.contains("#") { // I know, this isn't any sort of validation. It's an example.
Ok(Self { value })
} else {
Err(InvalidEmailAddress)
}
}
}
Ignoring that now new() behaves unexpected (it probably would be better to use a build() method), this brings an issue: When someone builds an EmailAddress through the constructor, it is guaranteed to be "valid". But when someone constructs it as normal struct, it may not be.:
let guaranteed_valid = EmailAddress::new(String::from("hi#example.com")).unwrap();
let will_crash = EmailAddress::new(String::from("localhost")).unwrap()
let unknown_valid = EmailAddress { value: String::from("hi-at-example.com") }
I would like to prohibit any users of those structs from constructing them directly like in the last line.
Is that possible at all? Are there any more ways someone could construct an EmailAddress in an invalid way?
I'm OK with placing the structs in a module, and using public/private visibility if that is possible at all. But from what I can see, any code that wants to now enforce the EmailAddress type, say a send_report(to: EmailAddress) would have access to the struct and can build it directly. Or am I missing something crucial?
You need to place your struct in a module. That way any code outside of that module will only be able to access the public functionality. Since value is not public, direct construction will not be allowed:
mod email {
#[derive(Debug)]
pub struct InvalidEmailAddress;
pub struct EmailAddress {
value: String,
}
impl EmailAddress {
pub fn new(value: String) -> Result<Self, InvalidEmailAddress> {
if value.contains("#") {
// I know, this isn't any sort of validation. It's an example.
Ok(Self { value })
} else {
Err(InvalidEmailAddress)
}
}
}
}
use email::EmailAddress;
fn main() {
let e = EmailAddress::new("foo#bar".to_string()).unwrap(); // no error
//let e = EmailAddress { value: "invalid".to_string() }; // "field `value` of struct `EmailAddress` is private"
}
Playground
More details on visibility in the Book.
I want to implement a module that encapsulates different types of messages with different types and quantity of fields that enables to send and receive them using the same send and receive functions, and then determine what variant of the message is, with what fields; using match.
I have the following enum and functions (simplified) :
pub enum Message {
Start { field1 : type1 },
End { field2 : type2 },
}
impl Message {
pub fn send( &self ) {
send(self);
}
pub fn receive( &mut self ) {
*self = receive();
}
}
I want to use them as follows:
Send:
let message = Message::Start;
message.send();
Receive
let message = Message;
message.receive();
match message {
Start{field1} => { ... }
End{field2} => { ... }
};
When calling the receive function, I get a compiler error that says "use of possibly-uninitialized message". It makes sense because this variable has not been initialized. How can I achieve this behavior with no errors?
It sounds like you're looking for an associated function which returns a Message.
impl Message {
pub fn receive() -> Message {
// Do whatever and return a Message::Start or Message::End as needed.
}
}
// To call...
let my_message = Message::receive();
Associated functions are sort of like static functions in C++, and they use :: on the type name itself as the call syntax. At runtime, it's just another ordinary function, but it's in the Message namespace so it's easier to find for programmers at write time.
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 have a struct as follows
type MyStruct {
EmbeddedFooBar
}
func (m *MyStruct) Foo(b *http.Request) {
// Doing something
}
func fn(args ...interfaces) {
// It's here I want to get my struct back and run the "Get" method
// Please keep in mind I am too pass a pointer param into the struct method
strt := args[0]
....
get struct back to static data type MyStruct
and run "Get()", dont mind how/where I will get *http.Request to pass, assume I can
....
strt.Get(*http.Request)
}
func main() {
a := &MyStruct{}
fn(a)
}
I am passing the struct above to a variadic function fn that expects ...interfaces{} (thus any type can satisfy the params)
Inside the function fn I want to get back my struct MyStruct to it's data type and value and run it's method Get that can also accept receivers such as *http.Request
How do I get My Struct back from the interface arg[0] and run the method Get of the struct with the ability of passing a pointer.
What you want is Type Assertion. Solution could something like this:
func fn(args ...interfaces) {
if strt, ok := args[0].(*MyStruct); ok {
// use struct
} else {
// something went wrong
}
// .......
}
It sounds like what you want here is a specific interface, specifically one that has a Get method that accepts a pointer to an http.Request.
For example:
type Getter interface {
Get(*http.Request)
}
func fn(getters ...Getter) {
getter := getters[0]
getter.Get(*http.Request)
}
func main() {
a := &MyStruct{}
fn(a)
}
This allows the compiler to check that the arguments to fn have exactly the methods you need, but it doesn't need to know anything else about the type. Read more about interfaces here.