📜 ⬆️ ⬇️

Rust and Swift (third, fourth, fifth and sixth parts)

I continue to translate a cycle in which the author simultaneously studies Rust and Swift and compares them to each other. You can find the translation of the introduction and the first two parts here . In this part we will talk about operator overloading, string manipulations and collections.


Operators, their overload and thoughts of brevity


I just got to the operators in Swift. First question: are the operators a special syntax or is it just sugar for protocols? Every modern language I use or have ever played with (Python, Ruby, Io, Elixir, and Rust are some examples from a wide variety of eras and styles) implements them simply as sugar for other language constructs.

Picked up, I found out that the operators are functions (ok), given global space Swift module. I say “okay” instead of “good”, because the explanation is this: this is the only way to make the operators work as binary operators between existing types. It misses the fact that in this case the reason lies in the structure of the language. It seems that the protocols would fit perfectly here, but perhaps they, unlike the traits of Rust, are not able to cope with the problem to the full? This is an open-ended question, and I have no idea how to answer it.
')
Interestingly, Rust has a slightly smaller number of operators than Swift, even if not considered as mentioned in my previous post. In Rust, as in Python, the prefix and postfix operators are completely absent, because the same results can be obtained in other simpler ways. In Swift, these operators are partially saved, no doubt because most programmers who dealt with (Objective) C are well acquainted with them and their idioms.
Translator’s note: in Swift 2.2, the increment / decrement operators were removed, which were not originally in Rust. Swift, on the other hand, allows you to create your own operators.

I also learned something new about operators in Rust: boolean operators || and && differ from bit | and & operators not only because the first group implements calculations according to a short scheme. Of course, using the second group, you can perform bit operations, but the reference book emphasizes the difference in terms of the computation scheme. This is quite justified, but I had never thought about it before.
Translator’s note: to be honest, I didn’t understand what the author wanted to say.

There is no ternary operator in Rust, which is related to the way the language works with expressions and statements. In Swift, he is. An interesting idea about the difference in linguistic design: Rust got rid of this operator, since the blocks of the if operator are expressions and, thus, it is redundant, and the creators of the language sought to remove unnecessary features. (A discussion about abandoning the ternary operator and an interesting remark about JavaScript from Brendan Ike read here ). I will make a reservation that this is not a criticism towards Swift, but simply a comment, although I really like the Rust approach using expressions.

On the other hand, I really do not like the operator ?? (nil coalescing operator). It looks like a contraction for the sake of contraction, partly due to Swift's desire for brevity of optional types. Sometimes brevity leads to a loss of clarity. Excessive cuts complicate the language and force slowing down when reading each line.

Boolean operators in the languages ​​being compared are no different and not surprising.

I wonder how many times the word “short” or its synonym is used in the book about Swift? I understand more and more clearly that brevity is one of the main goals of this language. Maybe I just think so, but it's still a bit strange. Brevity is good, but readability is much better.


Compromises in the structure of the language on the example of working with strings


Both Swift and Rust solve the problem of security and memory management, although they approach it differently: Swift through automatic reference counting, and Rust through the concept of ownership. For everyday work, the Swift approach seems more advantageous to me for the same reason as Python or Ruby: it's nice when they do everything for you. Rust gives a lot of opportunities, but at the same time makes you constantly think about ownership.

In other words, there are trade-offs in all languages. Although so far I like Rust more than Swift, I will no doubt find many points on which Swift will be better. You can't get everything.

I drew attention to this in part because Swift works with strings (or other types that are passed by value) much easier than in Rust. The results are almost the same, but since in Swift all the lines are passed by value and never by reference, you just do not have to think that they will be modified.

Of course, for this purpose in Rust there is a treyt Copy - I mean that Swift is a bit “ergonomic”.

Interpolation of lines in Swift is very convenient . This is the only thing I miss in Rust. Its Python-style macro for formatting strings is good, but interpolating values ​​( strings with \(variables) or even embedded expressions like \(2 + 4) ) is just fine.

In general, the Swift approach to strings is well thought out and pays due attention to detail, which greatly facilitates working with complex or "non-Western" languages. I, as crazy about typography, really appreciate it.

Moreover, since Swift strings handle all such boundary cases for Unicode, several standard string access patterns are lost, which makes it difficult (or makes it impossible?) To understand the internal structure of the string. Depending on the circumstances, this can be both an advantage and a disadvantage. As I said: compromises everywhere.

In fact, as I read further, I realized that Swift handles Unicode strings quite elegantly and provides insight into the process, using separate methods for different implementations. In particular, I appreciate that you can both work with the type String , and get direct access to the "code points" - and not to any one, but to any of UTF8, UTF16 or UTF32. Trust Apple's experience: the text must be treated very carefully.

The lines in Rust are not bad, but less intricate (presumably, this is done for the sake of simplifying their representation in memory). In this language, a String or str always consists of Unicode scalar values ​​(UTF32) encoded as a sequence of UTF8 bytes. In it, unlike Swift, there are no convenient methods for obtaining other implementations. At the same time, I believe that in everyday programming this will rarely appear, and perhaps never at all. It is important that both languages ​​store scalar values.

This is the first part in which I did not feel the obvious superiority of Rust over Swift. Some of the compromises in the structure of these languages ​​are more pronounced here, and I appreciate that Swift's “ergonomics” are among them.


Advantages (and difficulties) of parallel language learning


I study Swift for a couple of weeks, and before that, for a month, I plunged into the depths of Rust. Such an approach — mastering two languages ​​almost simultaneously — is completely new to me, and there are good reasons for that. It is not easy to learn a programming language, and in order to comprehend new knowledge, it is necessary to work a lot with it.

I do it out of necessity. I hope that I can develop an application on a very functional and effective cross-platform database of the Rust language, but I plan to release a native application for OS X only when everything is polished. My desire to make the core of the application portable immediately excludes the use of Swift. Honestly, this is facilitated by the fact that this is the language of Apple: I’m happy to use Apple’s tools on their platform, but I don’t want to constantly depend on the decisions of this company. In addition, the experience in Rust can be useful in many other cases.

So, I need to learn both languages.

And although in a normal situation I would not recommend - and if you do not have sufficient experience in programming and knowledge of several languages, even frankly discourage you from using such a method - it seems to me that it is incredibly useful. These languages ​​were created at about the same time and drew inspiration from the same sources, their audience and goals partly coincide. At the same time, as already shown in this cycle, in many respects they are quite different.

The parallel study of two languages ​​helped me to see which compromises each of them made, made me think about what caused their differences. In particular, I think that now I understand better what is happening “behind the scenes” of these languages ​​and, I know, what to expect from them. This, in turn, significantly influenced the speed of learning a language. Of course, the fact that I know several languages ​​has also played a role here, and recently I have been actively expanding my horizons: I read about Haskell, functional patterns in JavaScript, etc.

Of course, in both languages ​​I still have a long way to go. Reading at night and on weekends and playing a little with each of them is not the same as holding your teeth into a project and looking for pain points. Nevertheless, I am really glad that I study these languages ​​at the same time. If you are ready to accept the challenge, you can also try. You will be surprised how much you can learn.


Types of collections and the difference between syntax and semantics


I think the following sentence in many ways characterizes the Swift language design:


Although the two forms are identical from a functional point of view, a shorter form is preferred and is used in this manual with reference to the type of data set. —The Swift Programming Language (Swift 2 Prerelease)

The documentation for the various types in the Rust std::collections module is interesting and useful. Highly recommend.

While reading this chapter in the Swift tutorial, I noticed one thing: there are no named parameters in Rust, and there are some in Swift. In both cases, this decision is justified, but it seems to me that this is one of the details that I would not most brag about in Rust. Python spoiled me.
Translator's Note: There is an RFC for adding named parameters to Rust.

The Array type in Swift is similar to the Vec type in Rust (which is usually created by the vec macro!). Both containers can dynamically resize and store items on a heap, while arrays in Rust have a static size and are created on the stack. The syntax for creating arrays in both languages ​​is very similar (although the result is different):


 let an_array: [Int] = [1, 2, 3] //    var an_array = [1, 2, 3] //    

 let an_array: [i32, 3] = [1, 2, 3]; //  let a_vector: Vec<i32> = vec![1, 2, 3]; //  

This can be written shorter, since both languages ​​can infer types, so you rarely have to write that way. A more familiar option would be as follows:


 let an_array = [1, 2, 3] var an_array = [1, 2, 3] 

 let an_array = [1, 2, 3]; let a_vector = vec![1, 2, 3]; 

Rust also adds the concept of "slices", which provide access to parts of an array and represent a pointer to an array and size (number of elements).

Operations with arrays in Swift are quite logical and surprisingly vivid. In a good way, they remind me of list operations in Python.

The Vec container in Rust has a rich API, and that's not bad. I was a little surprised by the lack of a method for traversing elements, but then I discovered that it is present in the IntoIter structure in the same module for which the treater is implemented. As a result, the corresponding method returns an instance of the Enumerate structure. (I suspect that under the hood, the arrays in Swift simply implement the Iterable protocol, which in some way resembles the Rust approach.)

This is an example of what I often say: Rust does not necessarily place everything in one object, but rather spreads the functionality across several related structures, associations, or traits. This is a really powerful approach, but it does require some habit. In this respect, Swift’s structures and semantics are much more similar to the languages ​​I’m more used to, although the use of protocols gives more flexibility.

Notice that I was talking about semantics, not syntax. Swift and Rust are a great example of how very similar syntax can hide differences in semantics. Another example: compare the syntax and semantics of JavaScript and Java - at first glance, they are syntactically similar, but nevertheless there is a gigantic abyss between their semantics.

Both Set in Swift and its crude analogue of HashSet in Rust have a contains method that is very similar to the in keyword in Python. There is nothing surprising in the fact that both types implement many similar methods. This was probably to be expected, given the fact that the sets are a standard mathematical concept.

Due to more strict typing, both Rust and Swift require specifying the types used in associative arrays ( HashMap in Rust and Dictionary in Swift), although, of course, both languages ​​can infer types in certain cases. You cannot mix the use of different types of keys as allowed in Python, but in practice this should not interfere with you for two reasons:


  1. As a rule, it is not recommended to use keys of different types. As for me, this often indicates that you should think more carefully about the types and data structures used.
  2. I wonder if it is possible in rare cases, when appropriate, to use the generic type in Rust or Swift. I plan to clarify this issue later!

It would be great if Swift used a Python-like syntax ( {'': ''} ) to initialize the associative arrays. However, I understand why this is impossible: curly brackets are already used for blocks, and in Python there is no such problem, since it uses indents. But it is really convenient.

I understand why Swift designers used brackets to initialize sequences ( [...] ): this greatly simplifies parsing. As a result, at first glance it is difficult to understand what you are dealing with. This can be an array, a set, or an associative array.

This underlines the underestimated aspect of the design of programming languages ​​- readability. As if we, programmers, did not like writing code, in reality we spend a lot, probably most of the time, on reading it. Thus, although it is necessary to pay attention to the convenience of writing, it is also worth considering the ease of reading the code. The syntax and conventions adopted in the language form a large part of this.

The type of Dictionary in Swift is very similar to its counterpart in Python, up to the coincidence of the names of several methods. This is true for HashMap in Rust. This is not at all bad.


Afterword from the translator


I begin to doubt that taking on the translation of this series of articles was a good idea. The author has interesting thoughts that pushed me to learn more about Swift. On the other hand, he has too many superficial judgments, and he has to look for information himself. If I supplement the translation with my own notes, as I periodically try to do, they will have to be written on almost every item. The result is not a translation, but the devil knows what. So, I’ll probably dwell on these parts.

Source: https://habr.com/ru/post/280902/


All Articles