📜 ⬆️ ⬇️

Implicitness

Often, when I discuss the design of Rust on RFCs or in the internals forum with other users, I notice characteristic statements about the clarity. Usually something in the spirit of:


I don’t like the < X> because it is less pronounced. All magic is relevant in < Y> , and Rust is an explicit language, so < Z> should be used.

Such comments terribly annoy me, because they give very little useful feedback. They only argue that "explicit is better than implicit" (it is assumed that this is an indisputable statement), and that some design is less obvious than the alternative (although it is often not even given an explanation why the design to be criticized is less obvious), from which it follows that their approach is preferable.


In a note published earlier this year, Aaron tried to get to the bottom of the issue of disclosure by discussing the size of the context (reasoning footprint) . He tried to break down the concepts of "explicitness" and "implicitness" into its constituent parts in order to prepare the ground for judging the explicitness of the design of a particular opportunity. I want to present a slightly different view of the problem and try to outline in general terms what we mean by the word “explicit”.


English is a rather fuzzy language in which adjectives have sets of context-sensitive meanings, for example, as the word fuzzy is used in the previous sentence. The word "explicit" is also meaningful, so I can’t say for sure that someone is misusing this word. However, I propose to express my thoughts when discussing "clearness" more clearly, so that everyone better understands what is at stake.


What do I mean by the words: "Rust is an obvious language"


Often, being puzzled by the words "explicit is better than implicit", I just want to take the opposite side in this question, arguing that explicitness is bad and implicitness, on the contrary, is good. Although I think Rust is pretty obvious, but when I use the word "explicit", I mean something more specific than is usually understood by this word. My opinion: Rust is obvious, because you can understand a lot about your program just by reading its source code .


For example, here are some definitions of structures on Rust:


 struct Doggo { coat_color: Color, stamina: u32, love: u32, // NOTE:  true is_a_good_dog: bool, } struct Color(u8, u8, u8); struct TennisBall; struct Park { dogs: Vec<Doggo>, } struct Fetch<'a> { park: &'a Park, doggo: &'a Doggo, ball: TennisBall, } 

I can say quite a lot about how these structures will be located in memory, just by looking at their definitions:



An example of implicitness (in the context given above) is the exact order of the fields in these structures. Rust does not specifically determine the order of the fields in the structure, so that depending on the situation it can be optimized by rearranging the fields in some way. Usually you don’t need to know this order, except when working with unsafe code.


I would say that the similarity of many aspects of your code is usually very useful and is Rust's strength. But we must remember that in order to maintain it, we have to make compromises: for example, the compiler cannot voluntarily transfer data from the stack to the heap during optimizations.


Yet this is a very narrow definition of clarity. It means that having the source code at hand, I can answer some questions regarding this program. Now I want to break down the concept of "explicitness" into several more specific concepts and consider how they describe the capabilities of the language.


Other meanings of the word "explicit"


Explicit - does not mean noisy (verbose)


When discussing the introduction of a more lightweight syntax, I often see how some users declare it less explicit. Although, as long as the code contains the necessary information, the code is “explicit” in the sense indicated by me above. So this property I call "noise" .


One example is an introduction to operator language ? which is a bit shorter than previous macros try! . Some users expressed fears that because of this operator, it would be easier to overlook the early exit from the function. In this case, they wanted the syntax to be more noisy, not just obvious.


I believe that all return points from a function should be explicit, but not necessarily noisy. That is, if I want to figure out how the function returns a value, I should be able to do it, but this is not the first thing I’ll pay attention to when reading the code. On the contrary, especially when forwarding errors on the stack through the operator ? , the early exit is generally of little interest to me when reading the code.


Explicit - does not mean “burdensome”


Sometimes users say that the syntax of a certain resource-intensive operation must be cumbersome to discourage the use of it once again. For example, users might consider language less elegant to create an object on the heap as compared to creating on a stack.


Often in such disputes the word "explicitness" is used, although such a "syntactic salt" is completely orthogonal to it. In fact, we are talking about the greater "heaviness" of structures in order to show the undesirability of its use. For example, you can imagine the attribute [repr(boxed)] , which would mean that type instances always stand out on the heap. This could be a fairly convenient form of writing a common pattern:


 struct Catters { inner: Box<CattersInner>, } struct CattersInner { color: Color, pounces: u32, naps: u32, meows: u32, } //  repr(boxed)    : #[repr(boxed)] struct Catters { color: Color, pounces: u32, naps: u32, meows: u32, } 

Such an attribute will not make the code less obvious: you can still look at the definition of Catters and immediately see exactly the same information about how it is placed in memory. However, such an attribute really makes it much easier to place type data in a heap.


As before, I personally do not think that writing code to place variables on the heap should be burdensome. It doesn't seem to me that heap data should be the default behavior, but there are not so few situations where heap placement is preferable to being placed on the stack. Therefore, we should not annoy users or, especially, give them a reason to think that they are doing something wrong.


Explicit - does not mean manual


Also, the word "explicit" is sometimes used to indicate the need to write code in order for something to happen. Although, if something happens in a well-defined way and information about it is easily obtained from the source code, this is again “explicitly” in the narrow sense I have given earlier. Instead, it should be said that some actions are manual , as users need to explicitly request the desired behavior.


For example, imagine a version of Rust in which drop must be called manually (note that in the current Rust you cannot call this method, but for example, let's assume that it accepts self by value). In fact, it is even safe, because Rust still does not strictly guarantee the call of destructors.


 fn string_processing(string: String, numbers: &mut Vec<u32>) { substrings = string.split_whitespace().filter(|s| s.starts_with('$')); for substring in substring { let n = substring.parse().unwrap(); numbers.push(n); } //   ,    "" string.drop(); } 

If you delete the drop call line, the memory allocated for the line will leak. I think that it is obvious to everyone that such an approach would be worse. There is nothing wrong with the automatic calling of destructors, because you can always understand when this happens by simply tracking the end of the variable scope.


Explicit - does not mean local


In some cases, by the word "explicit" users imply the explicitness within a certain piece of code. This means that some information about the code should be clear from studying only a certain part of it. Moreover, it can be of any size - a module, a function, an expression, and so on. If something is clearly on a certain part of the source code, then this does not mean that it is explicit everywhere - the word “local” is much more appropriate here.


The implicit possibility of Rust, which is not local at the same time, is to allow methods. Look at the code:


 fn main() { let mut vec = vec![0, 1, 2]; let x = vec.len(); vec.extend([x, x + 1]); for elem in vec.into_iter() { println!("{}", elem) } } 

In this function, we call three different vector methods - len , extend and into_iter . Each of which takes self in its own way (by reference, by changeable reference, and by value). Two methods are defined for the Vec structure itself, and one is from the Extend type. Nothing of this information is visible when looking only at the reduced function, however, all this becomes “explicit” when considering impl blocks for Vec<T> .


On the contrary, the operator ? has such locality. You can imagine that all the functions that return Result , which are called from a function that also returns a Result , would be automatically applied to the operator ? (this is how exceptions work in similar Java languages). But we decided that it should not be necessary to look at the function interface to understand whether an implicit early exit would work inside it. I think this is a good example of useful locality.


Conclusion


In general, if during the discussion you are going to use the word "explicit", then consider whether you should say more precisely what you mean:



Each of these terms — explicit, noisy, heavy, manual, local — is appropriate for use in some cases, and inappropriate in others. Almost always, when choosing an approach to the implementation of functionality, it is required to make compromises. One way to decide on a choice can be to consider how it will affect (explained by Aaron) the size of the context .


So I ask you next time when you will discuss the explicitness of some kind of language functionality, more precisely, specify what kind of explicitness you are worried about and clearly explain why you consider your proposal more reasonable.


Many thanks to everyone from the rustycrate community who participated in the translation, proofreading and editing of this article. Namely: bmusin , mkpankov , vitvakatu and sasha_gav .


')

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


All Articles