The solution linked to in "Go has a suitable answer" isn't relevant, because it doesn't support virtual methods. You can do the same thing described there in Rust by having a base struct, embedding the base struct in any child structs (composition over inheritance), and implementing Deref/DerefMut on the child if you like.
Go has the exact same set of features Rust has here, no more: struct composition or interfaces/traits.
Regarding graphs: The best solution is option 3, using a crate on crates.io (petgraph is the most popular, but rust-forest is fine too). The preferred solution to having multiple types of nodes in the same tree is to use an enum or a trait. (Servo uses a trait for its flow tree, with downcast methods like as_block() if needed.)
I'm puzzled by the contention that the fact that all nodes have to be the same type is a problem with Rust. That's always true in any language. Even in C, you'd have to have your next/previous pointers have some type, or else you couldn't traverse the list!
But widgets do have the same type: Widget, which the author already uses as a trait object. I think he was getting confused by mixing &Widget and Rc<RefCell<Widget>>. Just declare a type alias for the latter, use it everywhere, and it should work. You would do the about the same thing in C++, I believe.
I don't know anything about Rust itself but I find it amusing that the author thought that six months should have been more than enough time for the community to agree on and implement a nontrivial change in how the language works.
Yeah. As a non-Rust developer I was reading the post with some interest and a little sympathy until I saw his timeline.
"Right, so they've been working on this potential change since 2016, and...wait. 2016?!"
If he'd said 2014 or earlier, I'd consider him to have a point. 2015 is asking for a bit too much, but I can still see his frustration; I'd call that a wash. But 2016? It destroyed his credibility with me, because it's clear he has no idea what he's talking about when it comes to developing a programming language.
I know that sounds harsh but...man. He's leaving Rust because they're taking more than six months to implement a massive change to a core language mechanic? Have fun working with absolutely any other language ever.
Yeah, most of criticisms I've seen of Rust (ergonomic issues, benchmarks, compile time, even integer overflow) will be dealt with in time. 1.0 means that Rust is stable but lacking features.
Exactly. If you read that thread you can see there's careful and ongoing technical discussion of the details necessary to implement the feature.
It hasn't languished, it's not just a bunch of +1s met with developer silence. What precisely is the author expecting the Rust team to do better on here?
While I do agree that six month is not quite long, there's another issue: in the RFC PR, most of discussions took place before Mar 27, which is 10 days after when the RFC was submitted. Then there were a few comments on on Apr 28, and nothing really happened until Aug 4, when someone mentioned they were interested in the RFC, and there only has been minor discussions after that compared to the situation before Mar 27.
I might be wrong but judging from this, I felt like we might never reach a conclusion on this topic even in a considerably long enough time, since it did seem to me that the core team is not interested in this feature.
From this point of view, I don't think it matters too much when we debate if six month is long enough, even if we waited longer, I'm not sure if the situation is gonna change when interest in this feature stays low.
> it did seem to me that the core team is not interested in this feature.
It was proposed by a core team member, even. Changing a language takes time. There's only so many people and so much work to do: the author of that RFC has also been working really hard on incremental recompilation, for example, which a lot of people have been waiting for.
To re-phrase someone from the Reddit, you can't win: if we don't add new features but fix bugs, people claim that you ignore feature work, but if you add tons of new features, people wonder why you're bothering to add new features when there's lots of bugs open. It's tough.
Rust seems to be a poor match for the way the author wishes to solve this particular problem. That doesn't mean that their design is wrong, or that Rust is wrong, but it does mean that mixing the two would require rethinking parts of the design.
> So a trait can define abstract functions, but can't access any underlying fields.
In Rust, a trait is a purely abstract interface to a type. It explains how you can use that type, but it knows nothing about the implementation.
I'm not quite sure what the design goal was with the Widget type, but the closest solution is to replace the member variable in the abstract type with some kind of accessor (and to replace the magic negative font sizes with an Option):
trait Widget {
// ADDED: Access whatever underlying font
// size you have stored in this type.
fn desired_font_size(&self) -> Option<i32>;
// ADDED: Access the theme of this object.
fn theme(&self) -> &Theme;
// High-level interface to get font size.
// (With a default implementation in terms of
// other functions on this trait.)
fn font_size(&self) -> i32 {
if let Some(sz) = self.desired_font_size() {
sz
} else {
self.theme().get_standard_font_size()
}
}
}
I'm not sure this is how I'd personally design this, but it should compile.
> What IS the idiomatic Rust way to do a cyclical directed graph?
Unfortunately, the correct idiomatic way is that you try very hard to avoid doing so. Rust is all about clear ownership, and it doesn't like circular references.
The usual solution is to put the cyclic graph code into a library, and to use pointers and about 20 lines of unsafe code. (EDIT: See below for a better solution.) It's not much different from the C++ solution.
The Servo team really wants GCed, safe pointer support in Rust, and design work is well underway. But it's going to take a while to stabilize.
GUI libraries are an interesting special case: They involve huge class hierarchies, lots of implementation inheritance, circular references, and tricky ownership. Most older OO languages were optimized for this case. Newer languages tend to favor flatter type hierarchies and far less implementation inheritance. Which means that traditional object-oriented GUI designs may be awkward.
> The usual solution is to put the cyclic graph code into a library, and to use pointers and about 20 lines of unsafe code. It's not much different from the C++ solution.
Well, petgraph uses vectors and indices to avoid unsafe code, and it's the most popular graph library on crates.io.
Really, the answer here is "use a graph crate on crates.io". If you don't know which one to use, the best answer is probably petgraph.
> The Servo team really wants GCed, safe pointer support in Rust, and design work is well underway. But it's going to take a while to stabilize.
We aren't going to use GC'd pointers for any of the trees except the DOM. GC is way overkill for this use case. The reason why we want GC support is not to make tree data structures: it's for integration with JavaScript.
A lot of problems in Rust seem to involve replacing (smart) pointers with handles. Sometimes you really do want to do that, but it's bad if it's a necessary kludge.
Like someone else said recently, it's a problem if you can't reasonably teach a basic computer science course in Rust.
"Safe" Rust works for almost everything in a typical CS curriculum.
But if you want to teach an operating systems course or build cyclic, pointer-based data structures, you'll usually need a modest amount of "unsafe" Rust. Using "unsafe" gives you access to real pointers, which work just like they do in any systems language.
It's just that Rust chooses to lock those features away when you don't explicitly ask for them. If you actually need "unsafe", it's no more dangerous or more difficult than writing C code.
I a few universities I know, including my own, cyclic data structures are on the curriculum, but nowhere near being taught as basic or intro stuff. That would lists, queues, trees, and hash-maps.
When I took my degree we had one full year of data structures,
Data Structures and Algorithms I & II, but they were taught on the third year (our degrees are 5 years).
It occurs to me that these courses have been teaching what might (should?) be considered a naive view of memory; you show me a traditional cyclic data structure and I'll show you a concurrency bug. I suppose that's fine in 1978 when no one imagined they'd be dealing with multi-core microcontrollers, but today, when you find yourself using a "systems" language for non-toy work and you reach for a tool as sharp as a raw pointer, those naive data structures are frequently (usually?) inadequate.
Cyclic data structures are in my experience very useful for concurrency. It's probably the number three good excuse for using a linked list, when you want to hold a lock for O(1) time with no shenanigans. But also, generally, anything that involves communicating information from point A to point B, and waiting on that information, involves some sort of cyclic reference between the sender and listener.
I have to completely disagree ... What are you even talking about? The inability to have circular links is deeply hobbling; they arise naturally in all kinds of circumstances.
> A lot of problems in Rust seem to involve replacing (smart) pointers with handles. Sometimes you really do want to do that, but it's bad if it's a necessary kludge.
It's not a kludge. It's how you prove to the compiler that you're using those structures safely.
If you aren't used to programming with compiler-enforced safety, you might be tempted to call working with the safety mechanisms a "kludge". But in reality all you're doing is doing things a little differently so that the compiler can ensure safety.
Using handles / indexes instead of pointers is a type hole and introduces similar problems of memory safety through the back door. The problems will be semantic in nature rather than more fundamental but will still be bugs.
For the article author, I'd recommend going down a virtual DOM like approach rather than traditional OO toolkit. Rust meshes a bit more closely with functional ideas than OO.
I am currently "attending" the above mentioned course by following along online. I am enjoying it very much so far. There have been three assignments so far, the first of which was to get rust installed and such. I think it would take most of the HN crowd less than a week to catch up with the course so that they can follow it for the remainder of the semester, even if they have a day job. The most experienced programmers might be able to catch up within just a few hours.
> GUI libraries are an interesting special case: They involve huge class hierarchies, lots of implementation inheritance, circular references, and tricky ownership.
I doubt they have to be. There are other ways to do GUI, see for instance how Light Table uses a database-like Entity Component System. https://www.youtube.com/watch?v=V1Eu9vZaDYw
I am fairly confident "someone" could write a composition-based UI. I know of no fundamental reason why that would be impossible, or even any more difficult than doing one with inheritance.
However, that does not solve the problem that all the mature existing UI widget toolkits are based on inheritance, and that causes a serious "impedance mismatch" with languages that don't have inheritance.
As I expect more languages to start privileging composition over inheritance, I also therefore expect this problem to continue to become more acute until "someone" finally solves it. However, GUI toolkits are huge and "someone" is likely to be a substantially-sized organization. The problem is going to have to become very painful before it is solved well.
Granted, current GUI toolkits are huge. On the other hand, I have worked with Qt, and this convinced me most of the complexity there was a blend of avoidable bloat and a long tail of features few people ever use (a bit like offices suites).
I'm pretty sure properly written GUI toolkits can be much smaller. As in, satisfying 90% of our needs in a couple thousand lines of code. (The remaining 10% might require heaps and heaps of code, but I'm sure it doesn't have to affect the core.)
It's only an intuition at this point (I have yet to implement my own GUI toolkit). But I have reasons to believe this intuition is right: http://www.vpri.org/pdf/tr2012001_steps.pdf
I'm pretty sure properly written GUI toolkits can be much smaller...
Given that as far as I know, there are no examples of tiny fully-featured GUI libraries (it's all either trivial like cgui or a massive bloated mammoth like qt/wpf/android), isn't that a bit of a rich statement?
Like, I'm sure you can write a procedural generator in a handful of lines of code that spits out the full works of Shakespeare. It's probably possible, if you have the right seed and the right algorithm.
...but practically speaking, how do you actually build one?
The same goes for UI libraries; certainly it should be possible (in theory) to have a minimal beautiful GUI libraries with an excellent API; but every attempt to build one seems to have failed.
Perhaps the problem domain is actually more difficult than you're giving it credit for...
vpri.org managed to squeeze a GUI toolkit and a document format and the whole compiler toolchain required for the language (including rasterization) in about 20K lines of code, and it's fast enough to run in a laptop.
I'm pretty sure we haven't explored the sheer depth of simplifications that can still be done.
I doubt that you'll find much..
They didn't release the code of their "crown jevel" (Frank, the 'word processor'), which makes me quite unimpressed about these year of research where the main result is a bunch of papers and demos but (nearly no) code (except for the OMeta part).
I believe they did release the source code, not far from here: http://vpri.org/fonc_wiki/index.php?title=Installation_Guide (may be outdated, you may want to ask the FONC mailing list, may not work on your machine… it's a proof of concept anyway, not a hardened engineering artefact).
> > What IS the idiomatic Rust way to do a cyclical directed graph?
> Unfortunately, the correct idiomatic way is that you try very hard to avoid doing so. Rust is all about clear ownership, and it doesn't like circular references.
> The usual solution is to put the cyclic graph code into a library, and to use pointers and about 20 lines of unsafe code.
The point about C++ is important. Cyclic graphs are hard in any non-GC'ed language because it breaks the ownership story.
Safe Rust allows one to do an incredible amount of things whole providing strong guarantees against the sorts of problems that plague lower level languages. Since this isn't suitable for absolutely everything, we have unsafe Rust to fill in the gaps in the small areas it's needed. Unsafe Rust isn't "bad", it just doesn't provide the same guarantees as safe Rust. And if something goes wrong, you can at least narrow down your search to unsafe areas of the code.
This isn't the sign of fundamental language design flaws. It's the sign of a phenomenally well-designed language, where the downsides of seldom-needed yet powerful features are limited to only those areas where they're used.
I'm not convinced that cyclic data structures are a feature that's exotic enough to warrant breaking the language contracts. This thread alone shows that it's a common occurrence in tree structures. There will be likely be other structures as well.
So I'd think a better way than saying "don't do that - or if you have to, you're on your own" would be to analyze use cases and see which scenarios the language can satisfy. (As another poster suggested in making "parent pointers" an explicit language feature(
Agree. The language's memory safety system needs to support more common use cases. At least:
- Back pointers. (They're not owning pointers, and they have a well defined relationship with some other pointer. The language may need some way to explicitly say that.)
- Collections where not all slots are initialized. (This requires simple proofs by induction as you grow a collection into previously allocated unused space.)
You can always use std::rc::Weak if you need a cyclic data structure with weak references. That covers, I suspect, the majority of the use-cases for these type of structures.
For real, honest-to-god, cyclic data structures — just use unsafe Rust. Again, unsafe Rust isn't "bad". It's just unsafe. More care will be expected of the code to ensure that it exports a safe interface, but nothing in the language stops you from doing it. There's quite literally no loss of expressive power.
And if something goes wrong, you can at least narrow down your search to unsafe areas of the code.
No. That's only true if the unsafe code presents a completely safe interface to its callers. If the safe code opens a hole in Rust's protection system, which is very easy to do, you can now have C-type no-idea-where-it-is bugs.
Yes, the crash can come from every line of code, but the origin of the bug is in the unsafe code.
That's why unsafe code has to be cleanly inspected to be sure it has a safe interface, and by reducing the dangerous area to only few lines, it is far easier to do.
All languages have design flaws. Thats why people keep making new languages. Arguing that Rust does not have design flaws is going to arouse more skepticism then anything else.
It is not mandated that you 'must' write safe code but that you 'can' write safe code. So there really is no fundamental assurance that rust and its libraries are in fact 'safe' by Rusts own definition in any meaningful sense.
You could make the same style argument that C is a 'safe' memory language also as long as you use automatic memory management and no malloc. Of course that would be a rather disingenuous claim to make.
Nobody's arguing that Rust doesn't have design flaws.
That said, people also need to understand that many such instances are not design flaw, but are instead design tradeoffs. The OP indicated that having to switch to unsafe Rust to represent a cyclic datastructure is a design flaw. My response is that it's a tradeoff that pays dividends in every other piece of code.
Can Rust improve upon the number of problems that can be solved with safe vs. unsafe Rust? Absolutely! Is it worth cordoning off a few cases that don't technically need unsafe behavior, to ensure all other Rust code performs safely? Emphatically yes.
> All languages have design flaws. Thats why people keep making new languages. Arguing that Rust does not have design flaws is going to arouse more skepticism then anything else.
As somebody who has used Rust since late 2012, I completely agree. It seems that most of the more sweeping claims come from those new and excited users - hopefully over time this will soften and become more reasonable. Hype is definitely something that needs to be kept in check though, as it more than often results in a back-fire.
It indicates that idiomatic Rust doesn't like cyclic references and thus you must step outside safe, idiomatic Rust to do that, it really just means that every language has an idiomatic way to do things to stay on that idiomatic road and not every way of doing things fits the idioms - they're still possible but not idiomatic.
Given how the ownership system works, it makes sense
You may say that that indicates a flaw in the ownership system, but it doesn't.
The ownership system can do certain things, but not all of them - it's designed to prevent the majority of common memory safety violations, but if it can't prove everything. That doesn't indicate a design flaw, it indicates a system limitation, same way a GC pause it's not a design flaw, but rather a limitation of a well deigned system.
Given that Rust was designed by a group known for writing web browsers, how does that work out in practice, given that the DOM is a deep bidirectional graph?
UIs work routinely by the organizational principle the author is attempting. Nested groupings of related functions that often interact in small groups.
And the thing is, a pointer to your parent doesn't semantically mean an ownership relationship anyway. It s in fact an extremely clear declaration of the ownership contract because while the owner can own many things, an object in this graph can have but one owner.
Now, I'm not sure how you would encode this into a compiler, so the discussion may have to end there as being impractical to implement.
> Given that Rust was designed by a group known for writing web browsers, how does that work out in practice, given that the DOM is a deep bidirectional graph?
I believe that the JS engine has ownership over the DOM, and pointers into the DOM are GC'd by the JS engine. This is part of the motivation for adding hooks for GC's in Rust. These posts [1][2] discusses this, although I think there is a more recent one. Edit: more recent one here [4].
> Now, I'm not sure how you would encode this into a compiler, so the discussion may have to end there as being impractical to implement.
This seems like a good use case for a Weak<> pointer [3].
Meh. Using Weak Types for this would be circumventing the ownership system to say what you mean and maintain it by hand. Indeed I believe the Rust Language is essentially stating that you can't manage these things by hand but a computer can.
The road to hell is paved with good intentions, and I know myself and my cohort well enough that I appreciate the Rust sentiment on this subject. It was the main feature that drew my attention.
I'm wondering if this is a concrete concept that could be build into the ownership system. Logically speaking, this back pointer only exists to allow objects that are part of a Composition to reason about their environment, or be reasoned upon by an outside observer.
Maybe some of this information already exists in the language internals and you just need a function that can expose the owner of record of the object... There can only be one, right?
The reason Servo in particular would like GC hooks is because JavaScript is GCd and we want to be able to pass around Spidermonkey-managed pointers safely. We have a framework in place that does this, but proper compiler support would be nice.
Servo doesn't need to worry about finalizers because our DOM structs never have nontrivial destructors (explicit Drop impls).
The blog post throws around some ideas for dealing with the finalized problem though.
The solution is to implement guis with composition and delegation rather than inheritance. It requires some ree-thinking and elbow grease, rather than just copying stale designs from 30 years ago, which may be why so many people avoid it, though.
Also note that there are actually several QML for Rust implementations out there, and while I've only done weekend toying at least qml-rust works well.
I hit the same wall, and opted to learn a new way of design after years of slavishly adhering to OOP.
There's not much that I miss from OOP, and I now see its utility as more about making the ability to program scale predictably with an unpredictable workforce.
You can drop something when you use it some noticeable amount of time, not just couple of weeks. Otherwise it's more "I tried but it's not for me". There's nothing wrong with it - somebody will love Rust, somebody will not, somebody will love even Go - it's ok.
So, you want class based polymorphism, and you want trees of objects with back references. Sounds like Qt to me. I have the feeling the author tried to translate his knowledge of OOP languages to Rust.
Bad idea. Try F# first.
Seriously: my current favourite language is Ocaml, and it would have basically the same problems. But I don't care, because I don't think like that. I have other ways to solve my problems.
The author seems to be confusing typeclass composition with OOP. They are only topically similar. (Personal opinion; typeclass composition is more principled than OOP, but a bit more rigid, which he is running into.)
There are several good ways to do this with type classes/traits. One way is to write a function
font_size : Widget t => t -> FontSize
And then in the widget definition have the "internal" font size defined, which the author seems to be assuming the existence of for some reason.
Additionally, in a language like Rust with ADTs, there's no good reason to return negative numbers to signify things like a missing font. He should be returning Maybe FontSize if he wishes to signify that an object may not contain a font size.
> The author seems to be confusing typeclass composition with OOP.
I agree and would like to expound on that idea. One of my biggest frustrations with inheritance in traditional statically typed languages (I program in C++ for a living) is that inheritance is performing two functions at once: code reuse and typing. Confusing the two seems to cause a lot of pain. Inheritance as a type system is describing the kinds of things the object can do. Inheritance is (usually) a sufficient condition to say that the types can be used interchangeably. Making inheritance the only way to express the type information often forces some very unfortunate code.
The author is trying to use traits as a code re-use mechanism. He wants the trait to be able to see into the implementation and be a function of the implementation's private data. If that were allowed, that would invite all of the pain of inheritance for that kind of trait. Types with a different internal implementation would end up being awkward at best.
No, it's supposed to teach people how to use the language. When a large fraction of your user base will be bringing a specific skill set, then you should generate documentation aimed at translating those skills!
I've picked it up and put it down a few times now. It is super humbling to feel like I've been reset to total newb status after decades of work with software, including OO, imperative (with and without GC), declarative and functional languages.
I'm still interested and motivated to learn more. I hope to get a chance to spend time on it at work because a free hour here and there isn't enough for me to absorb this stuff.
And confession time: I haven't even done too much fighting w/the borrow checker, I'm still slowly absorbing the type system and other general stuff.
Just keep plugging away and finish something. It took me three projects to get to the point where the cognitive load of what I no longer had to worry about exceeded what I had to worry about.
It's hard to describe, and to say "if it compiles, it works" is not 100% accurate, but I would say "if it compiles, it works, and if it doesn't work, then I know it's my own logic error". It's like Rust guides you into the space of correct solutions and cleaner code when compared to other languages.
The other thing: there are huge dividends you reap from spending time in Rust that are realized when you go back to other languages. If you've spent your life in managed runtimes or more loosely-typed languages, you come away better for the time spent in Rust.
> the cognitive load of what I no longer had to worry about exceeded what I had to worry about.
Yup, this has been my experience too. I started using Rust for my primary project last March and was worried a little at first about the risk of wasting too much time, but at this point I'm ~1.5x to 2x as productive as in C++11:
* essentially no more "head-scratching action-at-a-distance bugs" (I've spent N years in C++ and I still sometimes invalidate an iterator) -- as you say, any errors that still exist are higher-level logic errors, which are (in my experience) much more straightforward to track down.
* enums and match/destructuring and all the builtin types (e.g. Option, Result) have me thinking in a much more strongly-typed and principled way. So even logic errors tend to be less frequent because I break down the problem the 'right' way immediately. Algebraic datatypes let one describe a state space really succinctly.
Rust is one of those languages that really, strongly wants you to be idiomatic, but it pays off!
On trees: Can you use regular references for the children, and weak references for the link back to the parent? That's a valid structure that tears down properly. With strong backpointers, when one of the objects is deleted, there's a moment when there's still a live reference to it. That's invalid under Rust's rules, so you can't do that.
There's also the option to own all tree objects with some collection for allocation purposes, and use weak references for all inter-object links.
As for traits, Rust does seem to have an overly novel and complex type system, and I'm not going to defend it.
The only real difference between a weak and strong pointer is that weak pointers are less prone to leaking memory (in exchange for the possibility of "sorry I'm dead" being a response to accesses). As soon as you upgrade such a pointer you've created a cyclic graph.
He only way for Rust's type system to understand these kinds of access patterns in a way that doesn't require the insertion of "shared mutability" types (e.g. Mutexes) involves mutating the tree during traversals.
A simple version of such a pattern: a "doubly linked" list which is actually two singly linked lists. One "traverses" this list by popping nodes off one and pushing them onto the other. The user has easy access to the heads of both lists. You could probably do the same with trees but this is a pretty unpleasant pattern.
I meant reference counted strong pointers. Non-reference counted weak pointers make no sense from a systems point of view: in order to be memory safe you have to track the number of outstanding pointers, which is effectively a reference count, so you might as well be honest and use a real reference count.
I don't understand why the author wants to access fields from a trait. They can be accessed by the trait implementations, which makes sense, because the fields depend on the type.
Thanks! Here are the reason why trait fields are preferable over accessors in case anyone else is wondering:
1. The borrow checker assumes accessors can do anything, freezing all other fields. This does not happen with trait fields.
2. Accessors have to use dynamic dispatch in case of trait objects, resulting in worse performance.
It seems like maybe rather that just porting nanogui it might have made sense to do a bit more translation/conversion of the patterns used in it to what rust encourages/makes easy...
Wasn't really able to follow the bit about traits but:
It seems like if you want to have back-references you either would need to use Rc<RefCell<T>> or Arcs, which is not the end of the world.
The best way to do it would be to not have back references at all. Then you could simply have Vec<Box<Widget>> for children.
If I were going to build a GUI system in rust I certainly wouldn't design it to be OO. I think you would repeatedly run into problems. An entity component system or something like react might work better in rust.
> However, a trait has no knowledge of the underlying implementation. So a trait can define abstract functions, but can't access any underlying fields.
> ...
> Let that sink into you. My first reaction to this was "Sorry, what?!". While there are valid criticisms against OOP, to offer this as a solution is just silly.
This is a misunderstanding of what traits do. Interfaces in Java have no knowledge of the underlying implementation, either. And more generally, neither does an ABC: you can't use fields you don't declare in the ABC in methods declared in the ABC.
It is sad that Rust traits can declare only functions, not fields; so you must redeclare your fields as methods if you want to be able to use them as part of a default implementation (I think). Later the author explores this option -- but notes the "field methods" must be public along with the real interface of the trait. I am not sure what options Rust provides for making them private. For example, it might be possible to split the trait into a public and a private half. (But maybe not: https://github.com/rust-lang/rfcs/blob/master/text/0136-no-p...)
I do question the wisdom of the author's design at a higher level, because the author is using a trait for provide an implementation, not just an interface. If the implementation really applies to everything that matches the trait, it should probably be provided with a generic. If not, it is at worst tedious, but hardly limiting, to recycle a generic definition by calling it in an implementation.
I have been feeling that way ever since two years ago when I first looked at the language. I still don't understand why this language has such popularity. It's really bad for the reasons mentioned. C++ is a solid language where you can do everything you want and also write memory safe code in a clean way with unique_ptr. C++ is not perfect but to me no alternative come close yet, rust and go comprised.
C++ is not memory safe, and neither is uniq_ptr. Use after move on one causes a segfault. (Actually IIRC it's UB, but it usually manifests as a segfault)
C++ is not memory safe. unique ptr provides no protection against dangling references. Besides, you cannot use unique ptr to make backreferences, which is what this article is complaining about.
I've been observing in some stronger critics about Rust a persistent focus on OO features, usually the ones that programmers tend to rely upon and which they feel to be "lacking" in Rust - usually requiring a different structure or abstraction which the programmer is not familiar with.
The issue about parent/children pointers mentioned in this article seems more a lack of understanding on the concepts of ownership and the traits system, for instance. It's indeed a harder problem to deal with in Rust if trying to apply a OO mentality, but one can find a way out if trying to understand the language first.
I have my personal thoughts about parts is Rust that could be improved; nevertheless, I don't see any issue with us excursions, and consider it an evolutionary step which brings system programming languages to a new level.
What "non-OO" methods for representing cyclical data structures are you referring to? The only one I can think of is lots of indirection (e.g. how petgraph does it), which can be too slow (especially for systems programming). As far as I can tell, Rust's model for verified code is pretty hostile to cyclical structures, especially if you want inhomogeneous references (i.e. not everything is an edge on a graph). I don't think you can just sweep this under the rug as "lack of understanding" of Rust's better ways of doing things.
Someone should write a good book on Rust's approach to OOP and how it differs let's say from classic C++ when doing actual library design. Despite all the good documentation on Rust, this topic is really lacking clarity.
>In languages such as C++ or C#, this is an easily solved problem. You create an abstract class or a base class depending on language and inherit that in your actual class.
Oh cool an anti-pattern being described as an "easy solution".
I wonder how many of his problems could have been solved using the 'unsafe' keyword. I'm naively familiar with Rust through the various news postings, but know it has the unsafe escape hatch.
I think it would be fair if you also would disclose that you are working in Mozilla and that you are directly connected to Rust when defending it on HN.
Nah, most HN readers know that and the rest are smart enough to figure it out.
We appreciate your concern for the integrity of the community. On the other hand, posts like this lead to flurries of offtopicness, so their cost/benefit is usually poor. We detached this subthread from https://news.ycombinator.com/item?id=12474588 and marked it off-topic.
I think pcwalton's extensive knowledge on not just how to program in Rust, but the rationale behind various design decisions should indicate to a careful reader that he is involved in Rust.
I disagree. I can know various decision behind various designs from reading what's going on github. And i know some of those decisions in different languages other than Rust, which doesn't indicate that I am involved with them.
I have different opinion on that matter. Sure, some of his points are valid, I even agree with some of them but not all and not always. That's why disclaimers exist, for that reason. Especially when there are cases of glorifying your own product and bashing others.
Do you honestly feel like this comment would help a person to see your side of this?
It's a basic assumption that anyone who speaks positively about a thing is biased in favor of that thing. We don't need to point out every instance of that bias because it would dominate every discussion while providing no value because it is something everyone fundamentally assumes in the first place.
As i noted before i agree with some of his points which means that i don't think that all his opinions are only driven by his bias. But that doesn't change what i wrote before especially if one can distinguish between what's valid technical fact and what's not.
Sounds like overkill to me? Would a comment in a profile be sufficient?
(Disclaimer: I'm working on a web based brainfuck ide/compiler/optimizer for my own amusement, and thus probably have some kind of dog in some kind of fight here?)
I think an affliation heads-up is required if you are pissing on a competitor's product. And in such cases, HN does effective policing.
It is also possibly helpful if the commenter is elaborating on product that they are involved in to establish that their input is authoritative.
And not to give cause for inflated heads here :) but seriously, if you are seriously interested in this space, you sh/would already know the cast of mvps and thus major contributors such as Russ Cox or Patrick Walton (et al) would not require introductions.
Honestly, just about anyone who's read an HN thread or two on Rust should be familiar with pcwalton's involvement with Rust at this point. I also don't see why he should have to insert a disclaimer paragraph in every single HN comment he makes.
He can make it inside his profile. Then i am not anyone because i didn't know he was involved in Rust some time ago, i learned that by a coincidence some time later.
I am not claiming that he is pushing the product, just that his opinion can be biased, but then how would you want to push clearly technical product like Rust without technical discussion?
I think a point you haven't considered when talking about bias is bias going the other direction. That is, if he were to mark his, technically oriented mind you, posts with information that he works on Rust it's possible that it will instill a bias among readers. Regardless of his association with Rust as a project he knows technical details and that's what's important in posts like this regardless of potential bias.
Why do you think it would be fair? Are you implying he is biased? But aren't we all biased for our own pet technologies we advocated, use and convinced others to use?
To put it another way, is his argument or point invalid somehow? But then it would be invalid regardless if he worked for Mozilla or Microsoft or Google
Because humans are only humans, we are biased more if we are emotionally involved in something, more time you spent creating something, investing your time, more you are biased. It's about level of bias also, someone that is only using some technology is not so biased as someone that is creating one. It's psychology 101.
It sounds like you'd like a signal that allows one to more quickly switch off their brain, like network television prefixing party affiliation to politician's names. "Oh it is a R talking? Well as a D I can assume that everything said is wrong, no need to evaluate individual points."
No, for me it's making the brain to see the whole picture, not turning it off. If you choose to turn off your brain when you have more information about person that is giving his personal opinions then it's your own choice. I even wrote in other comment that i agree with some of his points which contradicts your "no need to evaluate individual points".
Go has the exact same set of features Rust has here, no more: struct composition or interfaces/traits.
Regarding graphs: The best solution is option 3, using a crate on crates.io (petgraph is the most popular, but rust-forest is fine too). The preferred solution to having multiple types of nodes in the same tree is to use an enum or a trait. (Servo uses a trait for its flow tree, with downcast methods like as_block() if needed.)
I'm puzzled by the contention that the fact that all nodes have to be the same type is a problem with Rust. That's always true in any language. Even in C, you'd have to have your next/previous pointers have some type, or else you couldn't traverse the list!