What's the standard way to create custom conversion between types in Rust? - type-conversion

If I defined some enum and wanted to create a parser from string to that type, is there something better than just:
impl TheType {
fn from_str(s: &str) -> TheType {
// ...
}
}

The right way for converting from a string / parsing text is to implement the FromStr trait. For the example from the question it would look like this:
use std::str::FromStr;
enum Failure {
ReasonOne,
ReasonTwo,
}
impl FromStr for TheType {
type Err = Failure;
fn from_str(s: &str) -> Result<TheType, Self::Err> {
unimplemented!()
}
}

For generic conversion that cannot fail, you should implement the std::convert::From trait:
use std::convert::From;
#[derive(PartialEq, Eq, Debug)]
enum MyEnum {
One,
Two,
Many(i64),
}
impl From<i64> for MyEnum {
fn from(val: i64) -> Self {
match val {
1 => MyEnum::One,
2 => MyEnum::Two,
_ => MyEnum::Many(val),
}
}
}
fn main() {
assert_eq!(MyEnum::from(1), MyEnum::One);
assert_eq!(MyEnum::from(2), MyEnum::Two);
assert_eq!(MyEnum::from(3), MyEnum::Many(3));
}
Conveniently, implementing From also automatically implements Into:
let one: MyEnum = 1.into(); assert_eq!(one, MyEnum::One);
let two: MyEnum = 2.into(); assert_eq!(two, MyEnum::Two);
let many: MyEnum = 3.into(); assert_eq!(many, MyEnum::Many(3));
For potentially failing conversion, you should implement std::convert::TryFrom instead. It's only available in Rust 1.34 and up though, before these versions you can use the implementation in the conv crate.

Related

How can I do variants in Swift? [duplicate]

I would like to have a variable, which can have multiple types (only ones, I defined), like:
var example: String, Int = 0
example = "hi"
This variable should be able to hold only values of type Int and String.
Is this possible?
Thanks for your help ;)
An “enumeration with associated value” might be what you are looking for:
enum StringOrInt {
case string(String)
case int(Int)
}
You can either assign a string or an integer:
var value: StringOrInt
value = .string("Hello")
// ...
value = .int(123)
Retrieving the contents is done with a switch-statement:
switch value {
case .string(let s): print("String:", s)
case .int(let n): print("Int:", n)
}
If you declare conformance to the Equatable protocol then
you can also check values for equality:
enum StringOrInt: Equatable {
case string(String)
case int(Int)
}
let v = StringOrInt.string("Hi")
let w = StringOrInt.int(0)
if v == w { ... }
Here is how you can achieve it. Works exactly how you'd expect.
protocol StringOrInt { }
extension Int: StringOrInt { }
extension String: StringOrInt { }
var a: StringOrInt = "10"
a = 10 //> 10
a = "q" //> "q"
a = 0.8 //> Error
NB! I would not suggest you to use it in production code. It might be confusing for your teammates.
UPD: as #Martin R mentioned: Note that this restricts the possible types only “by convention.” Any module (or source file) can add a extension MyType: StringOrInt { } conformance.
No, this is not possible for classes, structs, etc.
But it is possible for protocols.
You can this:
protocol Walker {
func go()
}
protocol Sleeper {
func sleep()
}
var ab = Walker & Sleeper
or even
struct Person {
var name: String
}
var ab = Person & Walker & Sleeper
But I don't recomment use this way.
More useful this:
struct Person: Walker, Sleeper {
/// code
}
var ab = Person
You can use Tuple.
Example:
let example: (String, Int) = ("hi", 0)
And access each data by index:
let stringFromExampleTuple = example.0 // "hi"
let intFromtExampleTuple = example.1 // 0

How to implement CustomStringConvertible on non-RawRepresentable enum conforming to protocol?

Consider the following code, which consists of:
A protocol, P
Two enums, E0 and E1 that conform to P
A function, f(p:) that takes an instance of P as an argument
import Foundation
protocol P { }
enum E0: P { case a }
enum E1: P { case b }
func f(p: P) {
print("\(type(of: p)).\(p)")
}
let e0_a = E0.a
let e1_b = E1.b
f(p: e0_a) // prints "E0.a"
f(p: e1_b) // prints "E1.b"
I would like to be able to print a string that describes the p argument in the form:
"Name of type implementing P.case of P"
As shown in the function f(), this works just fine and I get the output I want.
What I would like to do, if possible, is implement a CustomStringConvertible extension on P that generates the same string.
Is this possible?
Here's one approach that doesn't work:
protocol P: CustomStringConvertible { }
extension P {
var description: String { return "\(type(of: self).\(self)" }
}
This doesn't work because \(self) causes description to recursively call itself until the stack overflows.
Here's another approach that doesn't work:
protocol P: CustomStringConvertible { }
extension P where Self: RawRepresentable {
var description: String { return "\(type(of: self)).\(self.rawValue)" }
}
This approach doesn't work because E0 isn't RawRepresentable. There is some magic under the hood that allows Swift to print an enum's case name even when the enum has no rawValue, but I'm not sure how to access it. Using something like Mirror(reflecting: p).description prints the type of p, not the enum's case name.
Thank you for any ideas. As I say it can be done in a function that receives a P, but a CustomStringConvertible would be a lot cleaner.

Why 'there cannot be more than one conformance, even with different conditional bounds'?

I hoped that Swift gives me the ability to create an extension for type with specified conditions in where block. I imagined that I can extend the same generic type with different extensions dependent on concrete generic type value (T). But not. Following example demonstrates my problem:
protocol P {
associatedtype Prop
var property: Prop { get }
}
enum E<T: P> {
case single(T)
case double(T)
}
extension E: P where T.Prop == Int {
var property: Int {
switch self {
case .single(let o): return o.property
case .double(let o): return o.property * 2
}
}
}
extension E: P where T.Prop == String {
var property: String {
switch self {
case .single(let o): return o.property
case .double(let o): return o.property + o.property
}
}
}
struct Int4: P {
var property: Int {
return 4
}
}
struct StringHello: P {
var property: String {
return "Hello"
}
}
print(E.single(Int4()).property)
print(E.double(StringHello()).property)
Following error and note are the result of the compilation.
error: conflicting conformance of 'E' to protocol 'P'; there cannot be more than one conformance, even with different conditional bounds
extension E: P where T.Prop == String {
note: 'E' declares conformance to protocol 'P' here
extension E: P where T.Prop == Int {
Is it really impossible? Why? What can I do with my code to succeed?
Some details to demonstrate the problem in my real situation.
I have some generic enum, which is used with many different wrapped types.
enum Color<T> {
case red(T), green(T)
func map<T2>(_ transform: (T) -> T2) -> Color<T2> {
switch self {
case .red(let o): return .red(transform(o))
case .green(let o): return .green(transform(o))
}
}
}
Very often, I need different extensions for Color to conform it to different protocols depending on the wrapped type. Sometimes these protocols have the same base (super) protocol and as a result, I have the current problem. Sometimes I cant extend Color to conform this base (super) protocol for all deriving protocols because I need different implementations.
Is it impossible? Yes and no. It's not currently possible in Swift, as it has been implemented. It is in principle possible to be implemented.
The name for this is "overlapping conformances", and it was explicitly and purposely rejected. You can find the rationale in the "Alternatives considered" section of SE-0143 Conditional conformances. The TL;DR is: because it's really complicated.
Without knowing more about what exactly you were trying to use this for, there's not much direction we can provide.
As was described earlier you cannot just do this kind of extension. However, you can use hack like this:
protocol SomeExtension {
func doSomething()
}
extension SomeExtension {
func doSomething() {
print("Do nothing or error")
}
}
extension SomeExtension where Self == [String] {
func doSomething() {
print("String")
}
}
extension SomeExtension where Self == [Int] {
func doSomething() {
print("Int")
}
}
extension Array: SomeExtension { }
let stringsArr = ["a", "b", "d"]
let numbersArr = [1, 2, 3]
stringsArr.doSomething()
numbersArr.doSomething()
In console you can see
String
Int

Extending custom type where associated type is equal to Void

I'm in a situation where I have a custom type that contains an associatedtype. In the case where this is equal to Void, I would like to have some default behaviour (to make the call-site more convenient). I tried to boil the example down to:
protocol FooType {
associatedtype T: Any
var bar: (String) -> T { get }
}
struct Foo<T>: FooType {
let bar: (String) -> T
}
extension Foo where T == Void { // Compile error: "Same-type requirement makes generic parameter 'T' non-generic".
init() {
self.bar = { _ in return }
}
}
The idea is that, in the cases where the generic type is Void, it doesn't make sense (in my scenario) to pass in a function (named bar in the example). Therefore, I just want a default implementation for this function in this specific context.
When trying to do the above I get the Same-type requirement makes generic parameter 'T' non-generic which sounds very similar to what happens when one tries to restrict e.g. the Array type when containing specific types. A workaround for this is to introduce a protocol, but I cannot do that for Void. Is it possible to do what I want or is this currently a limitation in Swift 3?
As of Swift 3.1, the code posted in the question now works. That is, the following now works as wanted:
protocol FooType {
associatedtype T: Any
var bar: (String) -> T { get }
}
struct Foo<T>: FooType {
let bar: (String) -> T
}
extension Foo where T == Void {
init() {
self.bar = { _ in return }
}
}
let foo = Foo<String>(bar: { (t: String) in return "" })
let zoo = Foo<Void>()

Collection of <type implementing protocol> in Swift [duplicate]

As an exercise in learning I'm rewriting my validation library in Swift.
I have a ValidationRule protocol that defines what individual rules should look like:
protocol ValidationRule {
typealias InputType
func validateInput(input: InputType) -> Bool
//...
}
The associated type InputType defines the type of input to be validated (e.g String). It can be explicit or generic.
Here are two rules:
struct ValidationRuleLength: ValidationRule {
typealias InputType = String
//...
}
struct ValidationRuleCondition<T>: ValidationRule {
typealias InputType = T
// ...
}
Elsewhere, I have a function that validates an input with a collection of ValidationRules:
static func validate<R: ValidationRule>(input i: R.InputType, rules rs: [R]) -> ValidationResult {
let errors = rs.filter { !$0.validateInput(i) }.map { $0.failureMessage }
return errors.isEmpty ? .Valid : .Invalid(errors)
}
I thought this was going to work but the compiler disagrees.
In the following example, even though the input is a String, rule1's InputType is a String, and rule2s InputType is a String...
func testThatItCanEvaluateMultipleRules() {
let rule1 = ValidationRuleCondition<String>(failureMessage: "message1") { $0.characters.count > 0 }
let rule2 = ValidationRuleLength(min: 1, failureMessage: "message2")
let invalid = Validator.validate(input: "", rules: [rule1, rule2])
XCTAssertEqual(invalid, .Invalid(["message1", "message2"]))
}
... I'm getting extremely helpful error message:
_ is not convertible to ValidationRuleLength
which is cryptic but suggests that the types should be exactly equal?
So my question is... how do I append different types that all conform to a protocol with an associated type into a collection?
Unsure how to achieve what I'm attempting, or if it's even possible?
EDIT
Here's it is without context:
protocol Foo {
typealias FooType
func doSomething(thing: FooType)
}
class Bar<T>: Foo {
typealias FooType = T
func doSomething(thing: T) {
print(thing)
}
}
class Baz: Foo {
typealias FooType = String
func doSomething(thing: String) {
print(thing)
}
}
func doSomethingWithFoos<F: Foo>(thing: [F]) {
print(thing)
}
let bar = Bar<String>()
let baz = Baz()
let foos: [Foo] = [bar, baz]
doSomethingWithFoos(foos)
Here we get:
Protocol Foo can only be used as a generic constraint because it has
Self or associated type requirements.
I understand that. What I need to say is something like:
doSomethingWithFoos<F: Foo where F.FooType == F.FooType>(thing: [F]) {
}
Protocols with type aliases cannot be used this way. Swift doesn't have a way to talk directly about meta-types like ValidationRule or Array. You can only deal with instantiations like ValidationRule where... or Array<String>. With typealiases, there's no way to get there directly. So we have to get there indirectly with type erasure.
Swift has several type-erasers. AnySequence, AnyGenerator, AnyForwardIndex, etc. These are generic versions of protocols. We can build our own AnyValidationRule:
struct AnyValidationRule<InputType>: ValidationRule {
private let validator: (InputType) -> Bool
init<Base: ValidationRule where Base.InputType == InputType>(_ base: Base) {
validator = base.validate
}
func validate(input: InputType) -> Bool { return validator(input) }
}
The deep magic here is validator. It's possible that there's some other way to do type erasure without a closure, but that's the best way I know. (I also hate the fact that Swift cannot handle validate being a closure property. In Swift, property getters aren't proper methods. So you need the extra indirection layer of validator.)
With that in place, you can make the kinds of arrays you wanted:
let len = ValidationRuleLength()
len.validate("stuff")
let cond = ValidationRuleCondition<String>()
cond.validate("otherstuff")
let rules = [AnyValidationRule(len), AnyValidationRule(cond)]
let passed = rules.reduce(true) { $0 && $1.validate("combined") }
Note that type erasure doesn't throw away type safety. It just "erases" a layer of implementation detail. AnyValidationRule<String> is still different from AnyValidationRule<Int>, so this will fail:
let len = ValidationRuleLength()
let condInt = ValidationRuleCondition<Int>()
let badRules = [AnyValidationRule(len), AnyValidationRule(condInt)]
// error: type of expression is ambiguous without more context