TE
科技回声
首页24小时热榜最新最佳问答展示工作
GitHubTwitter
首页

科技回声

基于 Next.js 构建的科技新闻平台,提供全球科技新闻和讨论内容。

GitHubTwitter

首页

首页最新最佳问答展示工作

资源链接

HackerNews API原版 HackerNewsNext.js

© 2025 科技回声. 版权所有。

A Taste of Rust

235 点作者 cp9大约 10 年前

12 条评论

ufo大约 10 年前
The blog author mentions at one point that Algebraic Data Types cannot determine pattern exaustiveness for things like<p><pre><code> match 100 { y if y &gt; 0 =&gt; “positive”, y if y == 0 =&gt; “zero”, y if y &lt; 0 =&gt; “negative”, }; </code></pre> and wonders if there is some kind of &quot;algebraic data values&quot; to notice that the previous case is exaustive.<p>In Haskell they solve this particular problem with an &quot;Ordering&quot; ADT:<p><pre><code> data Ordering = LT | EQ | GT case (compare 0 100) of LT -&gt; &quot;Positive&quot; EQ -&gt; &quot;Equal&quot; GT -&gt; &quot;Negative&quot; </code></pre> Using a richer datatype instead of boolean predicates solves most problems. For some more advanced things you need extensions to the type systems. For example, to be able to say &quot;this function, when applied to non-empty lists returns non-empty lists&quot;, you need Generalized Algebraic Data Types (gadts).
评论 #9547442 未加载
评论 #9547758 未加载
pcwalton大约 10 年前
&gt; I’m not sure if the ownership rule is actually helpful in single-threaded contexts, but it at least makes sense in light of Rust’s green-threaded heritage.<p>It&#x27;s necessary for prevention of use-after-free. Here&#x27;s a simple example (which can be translated into the equivalent C++):<p><pre><code> let mut vector = vec![ &quot;1&quot;.to_owned(), &quot;2&quot;.to_owned(), &quot;3&quot;.to_owned(), ]; for element in vector.iter() { vector.clear(); println!(&quot;{}&quot;, element); }</code></pre>
kibwen大约 10 年前
The author&#x27;s observed speed discrepancy between iterators and while loops makes me think that they forgot to compile with optimizations, as the difference between those programs is almost negligible on my end.
评论 #9547511 未加载
评论 #9546895 未加载
评论 #9549494 未加载
Veedrac大约 10 年前
&gt; but five function calls (from_ptr, from_utf8, to_bytes, unwrap, to_string) just to convert a C string to a Rust string seems like an excessive amount of ceremony<p>Well, only the first four are really needed. The last one turns it into a local, growable copy. The rest should probably get a convenience wrapper, though.<p>&gt; the libxls API has an array-of-structs in a few places [...]; because Rust doesn’t believe in pointer arithmetic, I found myself manually writing the pointer arithmetic logic<p>Forgive me if I&#x27;m being stupid, but can&#x27;t you just use slice.from_raw_parts?<p><a href="https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;slice&#x2F;fn.from_raw_parts.html" rel="nofollow">https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;slice&#x2F;fn.from_raw_parts.html</a><p>&gt; writing things in a pseudo-functional, lots-of-chained-method-calls style, for which Rust is not all that well-designed<p>If this is about speed, then see the other comments. But if this is for another reason, what reason is it? Personally this style of programming suits Rust beautifully.
noelwelsh大约 10 年前
From reading the article I came to the conclusion the author is rather inexperienced with modern functional languages. Their claims about tension in the design of Rust between functional and imperative features for me mostly come down to them not understanding natural consequences of the modern statically typed paradigm, or Rust&#x27;s memory model.<p>For instance, take the discussion about if expressions. They claim an if expression with a single arm returning unit is unintuitive. Firstly, we should always recognise that claims about &quot;intuitive&quot; behaviour solely depend on one&#x27;s background. This behaviour is completely intuitive to me, coming from a Racket background (which behaves the same way). It&#x27;s also a natural consequence of having to give a type to an if expression with one arm. You have two choices: the else case either returns the bottom type (which operationally means it raises some kind of error), or it returns no interesting value -- which is exactly what unit is. Since a single arm if can only be used for effect, unit is the best choice here.<p>Rust is certainly a very different language to OO-ish imperative languages that most people are familiar with. I think the author made the common mistake of expecting Rust to behave like one of these languages, and then blaming the discrepancies between their mental model and actual behaviour on the language.
评论 #9549507 未加载
steveklabnik大约 10 年前
I am utterly thankful for new experience reports on Rust, especially for ones this well-written. Generally speaking, inaccuracies in such things are our fault, not the writers&#x27;, due to a lack of documentation and or good examples.<p>With that being said, a few notes:<p>&gt; It runs about five times slower than the equivalent program<p>I&#x27;d be interested in hearing more about how these were benchmarked. On my machine, they both run in roughly the same time, with a degree of variance that makes them roughly equivalent. Some runs, the iterator version is faster.<p>It&#x27;s common to forget to turn on optimizations, which _seriously_ impact Rust&#x27;s runtimes, LLVM can do wonders here. Generally speaking, if iterators are slower than a loop, that&#x27;s a bug.<p>&gt; Rust does not have tail-call optimization, or any facilities for marking functions as pure, so the compiler can’t do the sort of functional optimization that Haskell programmers have come to expect out of Scotland.<p>LLVM will sometimes turn on TCO, but messing with stack frames in a systems language is generally a no-no. We&#x27;ve reserved the &#x27;become&#x27; keyword for the purpose of explicitly opting into TCO in the future, but we haven&#x27;t been able to implement it because historically, LLVM had issues on some platforms. In the time since, it&#x27;s gotten better, and the feature really just needs design to work.<p>Purity isn&#x27;t as big of a deal in Rust as it is in other languages. We used to have it, but it wasn&#x27;t very useful.<p>&gt; But assignment in Rust is not a totally trivial topic.<p>Move semantics can be strange from a not-systems background, but they&#x27;re surprisingly important. We used to differ here, we required two operators for move vs copy, but that wasn&#x27;t very good, and we used to infer Copy, but that ended up with surprising errors at a distance. Opting into copy semantics ends up the best option.<p>&gt; how that could ever be more useful than returning the newly-assigned rvalue.<p>Returning the rvalue ends up in a universe of tricky errors; not returning the rvalue here ends up being nicer. Furthermore, given something like &quot;let (x, y) = (1, 2)&quot;, what is that new rvalue? it&#x27;s not as clear.<p>&gt; I’ve always thought it should be up to the caller to say which functions they’d like inlined,<p>This is, in fact, the default. You can use the attributes to inform the optimizer of your wishes, if you want more control.<p>&gt; It’s a perfectly valid code,<p>In this case it is, but generally speaking, aliasing &amp;muts leads to problems like iterator invalidation, even in a single-threaded context.<p>&gt; but the online documentation only lists the specific types at their five-layers-deep locations.<p>We have a bug open for this. Turns out, relevant search results is a Hard Problem, in a sense, but also the kind of papercut you can clean up after the language has stable semantics. Lots of work to do in this area, of course.<p>&gt; Rust won’t read C header files, so you have to manually declare each function you want<p>The bindgen tool can help here.<p>&gt; My initial belief was that a function that does something unsafe must, itself, be unsafe<p>This is true for unsafe functions, but not unsafe blocks. If unsafe were truly infectious in this way, all Rust code would be unsafe, and so it wouldn&#x27;t be a useful feature. Unsafe blocks are intended to be safe to use, you&#x27;re just verifying the invariants manually, rather than letting the compiler do it.<p>&gt; but until a few days ago, Cargo didn’t understand linker flags,<p>This is not actually true, see <a href="http:&#x2F;&#x2F;doc.crates.io&#x2F;build-script.html" rel="nofollow">http:&#x2F;&#x2F;doc.crates.io&#x2F;build-script.html</a> for more.<p>&gt; the designers got rid of it (@T) in the interest of simplifying the language<p>This is sort of true, and sort of not. @T and ~T were removed to simplify the language, we didn&#x27;t want language-support for these two types. @T&#x27;s replacement type, Gc&lt;T&gt;, was deemed not actually useful in practice, and so was removed, like all non-useful features should be.<p>In the future, we may still end up with a garbage collected type, but Gc&lt;T&gt; was not it.<p>&gt; Rust’s memory is essentially reference-counted at compile-time, rather than run-time, with a constraint that the refcount cannot exceed 1.<p>This is not strictly true, though it&#x27;s a pretty decent starting point. You may have either 1 -&gt; N references, OR 1 mutable reference at a given time, strictly speaking, at the language level. Library types which use `unsafe` internally can provide more complex structures that give you more complex options.<p>That&#x27;s at least my initial thoughts. Once again, these kinds of reports are invaluable to us, as it helps us know how we can help people understand Rust better.
评论 #9547759 未加载
评论 #9546987 未加载
评论 #9547177 未加载
评论 #9549763 未加载
评论 #9547155 未加载
chaoky大约 10 年前
the article brings up a good point with &#x27;systems language&#x27;. What is a systems language anyways? I guess C is, but whats the definition? Is common lisp a &#x27;systems language&#x27;? After all, a good number of operating systems have been written in common lisp, but is it too freewheeling and high level to be considered a &#x27;systems language&#x27;? Is java a systems language?
评论 #9547409 未加载
评论 #9547751 未加载
评论 #9547621 未加载
评论 #9549213 未加载
saosebastiao大约 10 年前
I too have found the assignment semantics to be a little baffling, and the errors to be ungoogleable (which may have changed in the last 4 months since I used it last). A pragma determining semantics seems quite brittle as well. I wish that there were some sort of distinguishing operators for copy vs move, much like how F# has different operators for initial assignment vs mutation.
评论 #9546873 未加载
imron大约 10 年前
&gt; But then, this won’t compile: let x = if something &gt; 0 { 2 };<p>Which makes perfect sense. After all, what should the value of x be if something is &lt;= 0?
评论 #9549390 未加载
lilyball大约 10 年前
It&#x27;s always interesting to see the experiences of new people to Rust. There&#x27;s a few curious misconceptions in here that I hadn&#x27;t seen before:<p>&gt; <i>Iterators are a great and reusable way to encapsulate your blah-blah-blah-blah-blah, but of course they’re impossible to optimize.</i><p>I&#x27;m very curious to know where you got this idea from. Iterators actually optimize very well, in most cases being indistinguishable from a manual imperative for-loop. If you forget to turn on compiler optimizations then you&#x27;ll see a significant performance difference, but that&#x27;s true for a lot of different things you might want to do. Compiler optimizations are important whenever you&#x27;re measuring performance.<p>&gt; <i>pragma</i><p>It&#x27;s not a pragma. It uses the same # sigil that C compilers use to introduce pragmas, but in Rust, it actually denotes an attribute which modifies the next item (or in the enclosing item with the #! syntax). This is used for a number of things. As you&#x27;ve seen, it can be used to automatically derive implementations of traits, and it can be used to mark a function as being a candidate for inlining, among other things.<p>&gt; <i>Rust rather inelegantly overloads the assignment operator to mean either binding or copying.</i><p>This is indeed a very curious misconception. Assignment actually isn&#x27;t overloaded like that at all. In your code example, when you say<p><pre><code> let y = x; </code></pre> You&#x27;re not binding y to the value of x, you&#x27;re actually <i>moving</i> the value of x into y. There is no implicit bind-by-reference in Rust. If you want a reference, you need to use the &amp; sigil.<p>The confusion here stems from the fact that some values can be <i>copied</i> and some values cannot. When you move a value in Rust, if the value can be copied (if it conforms to the Copy trait), then the original value is still usable after the move. This means you can say<p><pre><code> let x = 1; let y = x; let z = x; </code></pre> The line `let y = x` moves the value of x into y, but since the value is copyable, it&#x27;s really just moving a copy of the value. In this context, &quot;copyable&quot; basically means memcpy() can be used to produce a valid copy. There&#x27;s a different trait called Clone which has an explicit .clone() method that is used for values that require additional work beyond memcpy() to copy.<p>In your code example, your struct is not Copy, so moving its value makes the original value <i>inaccessible</i>. Basically, it&#x27;s considered garbage memory and cannot be read from again. This is why your code<p><pre><code> let x = MyValue::Digit(10); let y = x; let z = x; </code></pre> throws an error. It isn&#x27;t because `y` and `z` would be referring to the same value, it&#x27;s because after the line `let y = x;` the original value `x` is garbage.<p>So really, any time you do assignment like that (or any time you pass a value as an argument to a function) without taking an explicit reference (using &amp;), you&#x27;re doing a move. Values that confirm to Copy will move a copy of the value, and all other values will leave the original value inaccessible. This should actually be familiar to people coming from C++, where values that aren&#x27;t Copy are basically like std::unique_ptr, except that instead of leaving the original value with a known-&quot;zero&quot; state, the compiler prevents you from accessing the original value at all.
评论 #9547176 未加载
评论 #9547387 未加载
Manishearth大约 10 年前
This is a great post! :D<p>Some nits on various errors or misrepresentations:<p>&gt; pragma<p>You have some complaints about attributes (what you call pragmas) -- I suspect that many of them are due to you looking at them as if they were C++ preprocessor directives or pragmas. They aren&#x27;t, even if the syntax may be reminiscent :)<p>In some cases they&#x27;re like decorators in python (but much more powerful), in others, Java annotations. They&#x27;re a different concept.<p>&gt; Sadly, Rust is not a target for my favorite parser generator, and the lexers in Servo don’t look much better than C-style state machines, with lots of matching (switching) on character literals.<p><a href="https:&#x2F;&#x2F;github.com&#x2F;servo&#x2F;html5ever" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;servo&#x2F;html5ever</a> is the largest parsing library we use in Servo, and there are a bunch of Rust tricks done there. HTML parsing is <i>hard</i> (the spec is insanely complex), and this library does it well with much less code.<p>&gt; it would be nice if the Rust compiler got rid of the split_at_mut secret password and could reason sanely about slice literals and array indexes.<p>`split_at_mut` is just a library function that uses `unsafe` internally, much like many other core data structures and methods. It has nothing to do with the compiler.<p>&gt; It is also worth noting that processing non-overlapping slices in parallel is destined to come into mortal conflict with The Iterator, which is by its nature sequential.<p>I believe the thread::scoped API can be used to process nonoverlapping slices in parallel with a regular iterator. Not sure, but I recall seeing an example where this was done.<p>&gt; ...seems like an excessive amount of ceremony, at least for a language that keeps using the word “systems” on its website.<p>I don&#x27;t see what verbosity has to do with systems programming. Most of those are zero or low cost (so verbosity doesn&#x27;t correspond to more steps in the generated assembly); I believe there&#x27;s a utf8 check at one point and that&#x27;s it. Rust is verbose wherever errors or footguns are possible.<p>&gt; Rust won’t read C header files, so you have to manually declare each function you want to call by wrapping it in an extern block, like this:<p><a href="https:&#x2F;&#x2F;github.com&#x2F;crabtw&#x2F;rust-bindgen" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;crabtw&#x2F;rust-bindgen</a><p>&gt; but until a few days ago, Cargo didn’t understand linker flags<p>You can specify -L and -l flags in the .cargo&#x2F;config file under the rust-flags key (or output the same in a build script); this has been allowed for a while now. More complex linker args are now possible with the cargo rustc command.
评论 #9547577 未加载
评论 #9551648 未加载
Dewie3大约 10 年前
&gt; The pattern list looks pretty exhaustive to me, but Rust wouldn’t know it. I’m that sure someone who is versed in type theory will send me an email explain how what I want is impossible unless P=NP, or something like that, but all I’m saying is, it’d be a nice feature to have.<p>I Am Not A Type Theorist, but that looks like it could be very hard for a compiler to deduce in general. You might have to bust out a proof yourself for things like this. In which case you might not feel it is worth it for a &quot;nice to have&quot;.
评论 #9548308 未加载