Comparing Rust and C++
kukuruku.co
kukuruku.co
All in all, yes, Rust seems to be a nice language and in particular the template system looks somewhat more understandable, but please don’t write "Comparing Rust and C++" when you’re not actually using C++ but some weird mishmash of C and C++. If there are new, delete or pointer arguments in functions in your code, you’re most likely doing it wrong.
In short: none of the C++ code written in the article resembles what a well-trained, well-behaved C++ programmer uses. And the same cannot be said of the Rust code, I think. On the other hand: maybe the whole point of the article is something like 'you don't have to be super well-trained in Rust yet won't shoot yourself in the foot'.
std::vector<bool> features(const Widget& w);
...
auto highPriority = features(w)[5];
processWidget(w, highPriority); //whoops, undefined behavior
This is from Scott Meyers' new Effective Modern C++, in the chapter about why you should use 'auto'. Even the new 'safe' and 'convenient' features are loaded footguns.Not with GCC:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=18501
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55873
Obviously it should warn you, but when one of the most popular C++ compilers has been failing to report uninitialised variables for 10 years then I think that the issue is worth raising. Clearly significant sections of the C++ community don't place a high value on the accuracy of these kinds of compiler warnings.
This comes from the inherited C culture. After all lint was developed alongside the C compiler (in 1979).
How many C developers care to use static analysis?
You can use a std::unique_ptr after it is assigned to something else. That's basically a use after free.
> lost pointers to local variables shouldn’t occur when using references instead of pointers
Lost references to heap variables happen all the time in C++ when the target is deallocated before the reference is accessed. It's the same problem and Rust fixes it the same way.
> I don’t get the problems with "A dangerous switch" and "A broken semicolons", both seem sensible constructs
Really? Then you're being naïve or obtuse. Both are common sources of bugs in C/C++. Accidental failure to handle all inputs (common when additional inputs are added after the handling code is written) and subtle typos with major consequences (see Apple's "goto fail" bug).
The purpose of Rust is to eliminate causes of careless and accidental errors by forcing you to do things the right way every time (rather than permitting lazy code).
I don’t know, it seems reasonable to me to allow switch() not to handle all possible inputs and/or fall through. Requiring for() loops to always have a body also seems unnecessary, there are cases where everything fits nicely into the three standard elements.
As for the other things you list, I'd mark these more as "short-cuts", than features. It may take more effort to create code without them, but it gives parsing and code-readability improvements. Obviously the rust devs have made their decision here.
While true, you can make it throw in such scenarios.
I wouldn't be surprised if the debug builds of modern C++ compilers wouldn't do it already.
I compiled it using both:
g++ -std=c++14 -Wall -Wextra -g test.cpp -o test
clang++ -std=c++14 -Wall -Wextra -g test.cpp -o test
GCC v4.9.1 and Clang v3.5.0.Under neither case did it supply any warnings at compile time. In both cases it segfaults when it hits the second std::cout whilst running.
No, that's a null pointer dereference.
And heaven help you if nobody has yet whispered to you the secret of the one safe path. Or if you consulted an older textbook, and the idiom it taught has a grue.
As cute as your comment is trying to be, most languages cannot be learned by blindly trying things out, one should actually read a few books, try to be a part of the community and so on.
Take the C++ that's used on Chrome and compare to a Qt application. They'll be very different, and some features will be outright forbidden, depending on the codebase (for instance, Qt doesn't use copy constructors, or exceptions, and uses its own smart pointer classes). Not even templates, if you are using QObjects.
I know of noone that uses features such as RTTI.
Chrome and Qt's standards and design choices are their business and do not mean there is no recommended way to do things.
http://www.amazon.fr/C-Programming-Language-4th-ebook/dp/B00...
Note that they are both quite expensive. I am a student and it is not nothing to pay for this books.
Abe Books ( http://www.abebooks.com/ ) provides a good place to look for used copies of books. A significant portion of the books in my library have stamps on the pages indicating that they were discarded from really nice institutions.
Of course there are always free-to-read blogs, but unfortunately, good C++ blogs are harder to find than other, newer language blogs.
Effective Modern C++ is about the new C++11 and C++14 standards. I am reading it now, looks good so far.
If you can get one thing, maybe Stroustrup's book would be the best choice, but make sure you get the latest edition which includes C++11. Note that amazon.fr has the kindle versions at significant discount (there's also a bundle of Meyers' first three books) if you are ok with their DRM.
This is the best description ever. May I use it?
No, I think you missed the point, which is iterator invalidation.
http://stackoverflow.com/questions/16904454/what-is-iterator....
It's important to understand that various container methods can cause iterators to point to something else (or even nothing!).
In the example given, push_back could cause all the contents of the vector to be reallocated at a new address to make room for the new element. Any iterators pointing to the old address, including the one used in the for loop, don't get updated in this process.
More details for the interested:
http://kera.name/articles/2011/06/iterator-invalidation-rule...
In contrast, Rust only lets you have more than one reference to an object (like a vector and an iterator) if all of them are immutable. So any container methods that would cause iterator invalidation wouldn't be available inside of a for loop.
EDIT: Re-reading, I realize you might have already understood iterator invalidation, so sorry for the re-explanation, but I'll leave it up there for those that haven't heard of it yet.
I will point out that:
1. This sort of for loop is idiomatic C++ code.
2. C++ compilers don't even warn you when you push_back while having active const_iterators to your vector. Rust does, which is the point of the example. This is a case where C++'s const (logically won't change) is inferior to a stronger immutability guarantee, which Rust provides.
No. This is C code. In C++ you write:
std::vector<int> a = { 1, 2, 3 };
std::cout << std::min_element(a.cbegin(), a.cend()) << std::endl;
You shouldn't be writing a lot of loops in C++. You should be using the algorithms as much as possible. for(std::vector<int>::const_iterator it=v.begin(); it!=v.end(); ++it) {
if (*it < 5)
v.push_back(5-*it);
}
...you could do that with std::for_each and a lambda, or maybe some creative use of std::back_inserter, but you'd still have iterator invalidation problems.The right way to do it might be to use a while loop that uses std::find_if to find the next value to insert, but I'm not sure how you'd arrive at that solution without thinking about iterator invalidation in the first place.
...and the point is that Rust effectively says "you can't iterate over a mutable collection like that" while a less battle-scarred C++ developer might not even notice the problem before deploying to production.
it = v.push_back(5-*it);
Candidly, Modern C++ is an idiom adopted to work around the issues of raw memory management which Rust obviates out of the box.
What the author was writing was very standard introductory C++ taught in pretty much every C++ intro book I read in the early 2000s. That doesn't make it invalid C++, it makes it, "an older idiom".
To your more general point, C++ in the hands of an expert would clearly work better than presented here. I don't think that's an issue. The bigger question is, "Can C++ in the hands of a novice be as buggy as Rust in the hands of a novice".
True. But that would suggest that we also consider Rust as it was taught in the early 2000s. I don’t think this would get us very far.
> The bigger question is, "Can C++ in the hands of a novice be as buggy as Rust in the hands of a novice".
Then it would be useful to firstly point out in the article that the matter of comparison is not the whole language but merely its usefulness to novices and to secondly consider the validity of such a comparison when deciding which language to use in a new project – if most people are novices for most of the time they use the language (e.g. in introductory numerical computing course), then clearly “novice friendliness” is a sensible measure, but otherwise it’s mostly a moot issue how hard the first two months are if you end up using it for the next ten years.
On the other hand there are still a few constructs that I'm using using often enough in C++ that have currently no sane representation in Rust and that are astonishingly never mentioned in these articles:
- Everything that you use the friend keyword for. In Rust there is no way to access private data defined in other file (because that will automatically be another module which is also something I disagree with).
- Most things related to inheritance and interfaces. Composition is not always a better solution and Rusts traits seem still more suited for compile-time polymorphism than to runtime polymorphism. As an example try to find the equivalent of a `shared_ptr<SomeThing> thing(new SomeThing()); shared_ptr<ISomeThing> iface = thing` in Rust. And yes, I want to be able to use both afterwards.
- Enabling concurrency. Some people might disagree but I think Rust is currently mostly good at preventing multithreading issues - but not at providing good and easy ways to enable it. E.g. something like boost asio would be between hard to impossible to implement in Rust because the safety mechanisms disagree with callbacks that mutate state or Future objects (which are similar to callbacks). For some things using low-level primitives like channels (aka queues) and creating new threads might be fine, but there are also enough cases where you would prefer a simple singlethreaded eventloop.
All in all I think there are still enough areas where I think Rust still is (unfortunatly) no real alternative for C++. And as long as that's the case I think writing such biased articles isn't fair.
There are things like RefCell, which enables multiple threads to hold a reference to an object, with run-time checking of borrows. Or there are also explicit mutex wrappers that can be used. You could use these with channels, closures, or a callback system.
As Rust matures, we should see more of these kinds of features find themselves into the standard library.
RefCells do not allow multiple threads to hold a reference to an object because they are not Sync. They only permit dynamic enforcement of Rust's borrowing rules for references, through "interior mutability", as you said.
Yes, that's exactly the question I'm asking myself. I know that you can do everything in unsafe, but as soon as I step into that mode I'm getting the feeling that it might end up in the same level as C++ or probably even more complicated.
It probably shouldn't have to do with dealing explicitly with mutexes. If you have to I think that things like std::sync::Mutex are no good solutions because they are not really general purpose. If you really deal with low-level multithreading problems you often have multiple mutexes that might need to be locked in a special order and the mutexes are normally part of a data structure and do not own the data.
But multithreading is only a part of the whole problem. The other one is achieving easy-to-use and safe concurrency in a single thread (like Javascript APIs).
Rust is not OOP language, so it's logical that it can't do this. Rust got type system more like Haskell, and for me it's a big plus as I don't like C++ OOP that was taken from Simula and not even from SmallTalk. I don't like talking like this, but C++ OOP is complete trash(beat me for talking like this).
Also you are saying that Rust is not alternative for C++ in some ways trying to find what Rust don't have that C++ have. Those languages are completely different conceptually, but they target the same level. Rust is good alternative for C++ even now, taking for consideration that it's not stable(1.0 version will be soon, but it's not there yet).
There is no areas where Rust is no alternative for C++, because you can tackle any low-level problem with it, that you can tackle with C++. I don't want to say what language better, because it's not the best comparison. For now, all I can say that Rust is more sense full and more conceptually accurate, it's syntax is more concise and easier to understand than syntax of monstrous language like C++.
For the first in the long time we can say that we got low-level language with ability to write code using high-level abstractions and it will be effective. We don't need to drop code understanding for low level optimizations, yet we have power to fully optimize bottlenecks.
It's not silver bullet for sure, but guys did a big job here, showing that low level language can be more clear for human.
Not sure what you're trying to do there, but Rust can certainly alias one object with more than one trait reference. In this particular case, you're using two mutable references to the same object, which Rust doesn't allow. But if both were immutable references, there wouldn't be an issue, assuming the right declarations preceded your code.
There are quite a lot of cases where you would like to have things like this, but the example that's probably most straightfoward to understand is GUI systems:
E.g. you have a generic `Widget`, the `IWidget`. Then you have some base-class `Container` which contains multiple other widgets (`list<IWidget>`). This might contain a reference to a `TextBox` (which is an implementation of `IWidget`) and another widget (or controller or whatever) might contain also a reference to the same thing. Both might need to be mutable. And in exact that moment you have code like the described one.
That's no borderline usecase, it's a thing that you in all popular HMI frameworks (and in C, C++, C#, Java, ...). Maybe you can solve the issues in a completly different fashion, with resorting to only immutable objects (like some approaches for ClojureScript). But these are then no longer zero-cost abstractions versus current technology and they are not that easy to pick up for most mainstream programmers (which expect C++ alternative and not a Haskell alternative).
Besides that people are already asking for bindings to things like QT - and that will require similar constructs.
In fact, it supports it more flexibly since different widgets don't have to share the same methods and data at all... well, unless you want them to. And you can have your widgets implement more than one trait without having to combine them all into one giant class definition.
Maybe you should read this: http://doc.rust-lang.org/guide.html#traits
As I said, the objective is to have access to one object (in it's native form or in an interface (supertype) form from different locations. For that you can can use Rc. To be able to mutate it you can add RefMut and end up with Rc<RefMut<T>>, e.g. Rc<RefMut<IWidget>>. And now how do I get an Rc<RefMut<TextBox>> or any other type like &mut TextBox back from that?
Admittedly this is ugly, and unnecessarily slow. For a more general solution, I think the language would allow implementing some kind of RcTransformed type which, given 'T, U : Unsized?, Transform : Fn(&T) -> &U' (or possibly a more general type, but I think that would require HKT?), takes an Rc<T>, calls the transform, and stores both the reference and the transformed reference, allowing direct access to the latter. This would require an unsafe implementation, but would be safe to use.
Alternatively, there may be some existing functionality like that that I just haven't heard of...
The main Rust community tries to discourage Rust "zealotry." For example, on /r/rust, the rules include:
4. When mentioning other languages, keep the discussion civil. No zealotry!
Anyway, as for your other question:
E.g. something like boost asio would be between hard to impossible to implement in Rust because the safety mechanisms disagree with callbacks that mutate state or Future objects (which are similar to callbacks).
I know that futures have been implemented several times in Rust, including in the standard library:
http://doc.rust-lang.org/nightly/std/sync/struct.Future.html
In general, multithreading primitives can be implemented using ordinary Rust libraries. Internally, these libraries use unsafe to do low-level threading work, but externally, they provide a safe API. The library author takes responsibility proving that their design is, in fact, thread safe.
If you know of specific boost asio features that can't actually be implemented safely in Rust, I'd love to know about them. I know that various people want to work on async I/O libraries for Rust after 1.0, and it would be nice to know about any major obstacles in advance.
This article still reads like zealotry. No tradeoffs are shown, only praise. C++ examples are alien. Like GP, I had the same experience with C++ templates being able to do more things than Rust's traits (like templating by a value).
These are futures that you only can synchronously wait on (like what is currently available as std::future). However there is no possibilty to attach continuations that run when the value of the future is available. E.g. like the C++17 concurrency proposals, C#'s `Task` type or Javas `CompletableFuture`. These all rely on storing some kind of callback or closure inside the Future. That's not easy in Rust because it leads to situations where ownership is no longer obvious. The continuation might need to capture objects that are still needed locally (so you need something similar to Rc<RefMut<SomeThing>> ). In the most simply example's the continuation needs a mutable reference to the Future itself whereas the Future-producing operation also has one. And if you have possibilities that the continuation could run on another thread/executor than the calling thread (which is possible with the other languages) things get more complicated.
Regarding asio. You don't have to deep dive very much here: Simply consider boost::asio::async_write(socket, buffer, [](error_code, bytes_transferred) { /* Do something with the result */ });
You need some mutable objects (like socket or the object that contains socket) locally AND you need acccess to them in your continuation. This will only work "safely" with garbage-collected or ref-counted types and these do still not seem to work for interfaces.
I'm not quite sure if I understand the problem you're describing. I can definitely share mutable objects between threads, and I can do so using abstract traits (aka "interfaces"). Here's an example:
https://gist.github.com/emk/acdf3ab9c79ba1abe6d2
I had to use a "box" here, because that's the easiest way to store objects of varying size that implement a specific interface. Experienced Rust developers may be able to simplify this a bit.
Is this what you were thinking about? Or did you mean something else?
See the disucssion a little bit below for examples with Rc (it's the same problem but within a thread instead of between threads).
1. what is the technical difference between template and generics? 2. how do we know that what Rust has are generics? 3. considering that the Rust type system was recently demonstrated to be Turing-complete, how can it be less powerful than C++'s?
I'm personally not convinced that the extra possiblities and extra complexity of C++ templates vs. a good generics implementation in other languages is worth it, but I just wanted to highlight that the comparison is not fair. And hopefully we will get better error messages with C++ concepts, which will probably compare better to Rusts generics and kinds.
[1] http://en.wikipedia.org/wiki/Compile_time_function_execution... [2] http://en.m.wikipedia.org/wiki/Substitution_failure_is_not_a...
As a Rust fan, I don't think we need to resort to cheap shots. The language stands well enough on its own; misinformation and propaganda pieces won't get us anywhere.
And nobody who's been writing C or C++ for more than five minutes would actually make that mistake with a switch statement (omitting breaks). On the contrary, its fall-through behavior is often very convenient.
In Rust, the equivalent might be:
let ioResult = File::open(&Path::new("foo.txt"));
...no unsafe pointers or mandatory allocation necessary (1). Plus you're forced to check your error condition by unwrapping the returned value.I don't bring that up to pick a nit, but to point out that this is basically the state-of-the-art (well, the char might be paired with a size_t) for passing strings through C++ APIs.
(1) I'm not sure whether Path::new actually allocates, but there's nothing stopping you from implementing a tight-and-fast InPlacePath that works seamlessly.
I see very little evidence that most programmers actually care about the advantages Rust the language, as specified today, brings. I'd contest that these days the biggest reason competing languages like Go, Rust, Python, PHP and Java often trump C++ is less to do with the language, and more to do with these languages having sizeable and fairly decent standard libraries. Programmers want to get shit done.
The C++ standard library is tiny and has seen fairly paltry growth over the years, with only modest introductions in C++11 of things like regex and threads. If a set of filesystem, HTTP, networking, and serialization libraries were part of the standard, and well implemented, we may well see a different landscape.
And a type-safe dependency injection framework wouldn't be the worst thing in the world either.
Not that I'd turn my nose up at standard HTTP support.
For webdev I've personally used CppCMS, which is of good quality, although it is a framework and not a toolkit (it's MVC and the views want to use inheritance, has its own networking and template framework etc)
C++ has the advantage of an incredible amount of code written in over the years that mostly works. It also executes fast. It's also easy to find developers for it. With the new addons to the specification it's even possible to write readable code with it.. However, it has a stupidly long, verbose and feature creepy standard (well over 1000 pages), which results in no developer reading it. Because of it's design, compilation times tend to take long (as a ridiculous amount of headers are included even in the most simple applications).
Go is pretty much the opposite. It has a lean and readable spec (on purpose) and a fairly common canonical syntax without much discussion about it. Because it's relatively new, there is not much written in it however. It has some specific strengths, like a concurrency implementation that abstracts most of the process/thread/mutex stuff. With this, it does appeal to new projects that don't depend on libraries written in other languages. There is no point in porting multi-million SLOC applications to Go though, as that would be incredibly time-consuming. It has the potential to surpass C++ in a few decades though, as it operates in the same domain (systems programming).
Python is a versatile scripting language. It has nice syntax and is incredibly flexible at manipulating data. Add to it the well documented, feature-rich standard library. With it you can evaluate data with incredible flexibility and it has a fairly complete feature set for general programming tasks that span multiple OSs. The downside is that it's slow in execution and needs the interpreter along the scripts. Especially for smaller tasks and simple automation it is the first choice however because of it's small footprint in constraints (you can just edit and drop a scriptfile anywhere on a Linux system and it works).
Java.. I'm not entirely sure what Java does well. I hear praise for the JVM, but generally I get the impression that it is plagued by legal issues, alien looking GUI tools, more than verbose syntax and it also has a stupidly long specification (almost half of C++'s). Maybe someone can shed light on this one. Tools maybe?
> Java.. I'm not entirely sure what Java does well.
* Package/module system is dead "drop your JAR here" simple.
* Humongous standard library.
* Oracle provide a really powerful free JIT (probably still one of best for real world performance when it comes to JITing static languages)
* Standardised language specification (like C and C++)
* A well specified, really simple bytecode that other compilers and runtimes can target.
* It's a newbie friendly, procedural, object-oriented language that encourages 'patterns' that make developers feel comfortable and that they know how to tackle problems (whether it's the right way is another matter)
* Relatively safe, meaning there is less to learn (no pointers, memory management, arithmetic overflow etc).
To be fair, if I were wanting to find all matches for a regular expression in a given text file, I sure wouldn't use C++, even though C++ is certainly capable of doing that.
> [Go] It has the potential to surpass C++ in a few decades though, as it operates in the same domain (systems programming).
Nope, Go and C++ operate on different domains, Go is garbage collected and nobody will use it for low level system programming.Rust actually has a pretty small standard library. Since we have a package management system so early in the lifetime of the language, we're leaning on that rather than having a large standard library.
Just two days ago, a PR was opened to remove four more things from the standard library: https://github.com/rust-lang/rust/pull/19820
It's not comparing C++ with Rust, or at least not fairly. It's just listing the problems of C++ that Rust is trying to solve. Someone well versed in C++ could easily write an article with the same title and list a dozen reasons why C++ is superior.
I realize that Rust was designed to address the issues with existing systems programming languages, but surely it has flaws, tradeoffs and small annoyances, like every other programming language.
I really like Rust and see the value in what they are trying to achieve, but I'm realistic enough that it won't be perfect.
Rust core team member here: it absolutely does. Rust is _far_ from perfect, as much as I love it. Anyone who tells you Rust has no flaws is lying.
Does anyone know where to read about how Rust handles this?
* C++ has more robust compile-time meta-programming facilities, at least for the time being.
* C++ can generally include C (well, C89) code as-is with no translation layer. Rust's FFI is pretty inoffensive, but it's more work than a #include
* It's easier to do the really dangerous stuff if you have a good reason to. You can actually call push_back on a std::vector without invalidating your iterators if you're careful. I've never seen it used properly in production code, but it's possible. At any rate, it might be more awkward to write equivalent code in Rust. Then again, maybe a deque is better than a vector for this use case, in which case I suspect that Rust is capable of a more optimal solution than push_back or std::back_inserter.
(those libraries need to expose a C interface for Rust to be able to)
The only allocation that really needs "unsafe" is "Vec", which is needed to have some way to convert raw storage into variable size arrays. Everything else could be built on top of "Vec".
If you want the effect of an arena, you can have all the objects in the arena owned by a master object, and all the links within the arena weak, using "std::rc::Weak". This is a reasonable way to do a GUI or a web page, where there's heavy interlinking to neighboring graphical items, but those links don't indicate ownership. When the window or page closes, just drop the master object and the arena goes away. Yes, you spend more effort on deallocation. That's not where the time goes in a GUI.
There's an argument for doing things that way, rather than sprinkling "unsafe" around the library. Make it solidly reliable first, then profile, then look into compiler optimizations to make it faster.
(There's a macho mindset with some programmers. They think they don't need all that checking. They're wrong. Put those guys (it's usually males who have that attitude) on fixing obscure bugs or doing crash dump analysis for a while. They'll learn.)
The compiler is not a magical fairy that optimises away the cost of memory allocation. You don't 'emulate an arena' with a vector of reference counted pointers, any more than you emulate a cheetah by painting spots on your body.
Nobody uses an arena because they are lazy or hate destroying objects; you still pay the cost if your objects have the Drop trait. You use them because you have a bunch of fixed size allocations all with the same lifetime, and - after profiling - you need to exploit that fact.
There will be unsafe code outside of Vec because you need to do four things:
- allocate a chunk of memory (for non-relocatable objects) - construct objects in portions of the memory of that chunk - destruct objects in each chunk - deallocate the chunks of memory
If Rust ever gets placement box we can do the whole thing safely, but IIRC there hasn't been a decision made nor any experimental implementation. Until then, if I need to turn off the safeties to get something done, I will - not to spite the condescending 'enlightened' who are above getting their hands dirty, but because if there is a business need and it can be done correctly with due diligence, it is not 'macho', it is the professional thing to do.
It's worse when the subject is a nascent project and people's ideas are forming out of discussions like this, but in general it has a chilling effect on the subject being discussed. In this particular example, we need to know the deficiencies of Rust's allocation model, and there is much to be gained by discussing them and finding a workable solution. Shutting down the discussion with 'use the safety tools you already have or you're a foolish brogrammer' is not acceptable.
It's worthwhile to separate the two. If you can't do it at all, there's a problem with expressiveness. If you can't do it fast, there's a problem with performance. It's useful to try to do things without using "unsafe" to cheat the system. Then you find out what the language needs to do faster. That's a trouble spot to be identified. There may be an optimization which leads to a safe and fast solution. Or there may be a way to make something checkable so that it's not necessary to do something unsafe.
To get around this restriction you use unsafe block or function.
That is equally true of C++; you're completely safe as long as you don't make any mistakes.
A concrete example of safety is that Rust can tell you if you reference memory from an arena after the arena goes out of scope. C++ can not.
In practice, most applications have hardly any unsafe code, delegating all of their unsafe behaviour to libraries that have presumably been thoroughly tested.
There is a world of difference between this block/function is unsafe rest is enforced by compiler and everything written is unsafe, lets download third party program to check.
http://doc.rust-lang.org/src/arena/lib.rs.html#353-363
I wish languages would let you say the whole file/module is special - for example, inline all functions.
Are there other issues that you had in mind?
if (cond); //oops, a semicolon
alwaysHappens();
, thank you very much.