I think the article makes a good point, but the actual example isn’t Rust’s worst, not even close. It gets really hard to follow code when multiple generic types are combined with lifetime markers. Then it truly becomes a mess.
For my own situation, the articles present the right way to express all possible performance/error handling (which is expected in a standard lib) and then goes on to show how I actually code it in my own softawre where I don't really need the level of detail/finetuning of the standard lib.<p>Interestingly, my life starts at the end of the article, with the simple verison of the code, and as my understanding of rust widens, I go up to the beginning of the article and better define my function...
If I understood all the semantic properties, including the separate compilation requirements, correctly, here’s how I think it would be done in Swift with the proposed nonescapable types features (needed to safely express the AsRef concept here).
(Note that this doesn’t quite compile today and the syntax for nonescaping types is still a proposal.)<p><pre><code> @usableFromInline
func _read(pathView: PathView) throws(IOError) -> [UInt8] {
var file = try File(pathView)
var bytes: [UInt8] = []
try file.readToEnd(into: &bytes)
return bytes
}
@inlinable
public func read<Path>(path: borrowing Path) throws(IOError) -> [UInt8] where Path: PathViewable, Path: ~Copyable {
try _read(pathView: path.view())
}
// Definitions...
public enum IOError: Error {}
public protocol PathViewable: ~Copyable {
func view() -> PathView
}
public struct PathView: ~Escapable {}
public struct File: ~Copyable {
public init(_ pathView: borrowing PathView) throws(IOError) {
fatalError("unimplemented")
}
public mutating func readToEnd(into buffer: inout [UInt8]) throws(IOError) {
fatalError("unimplemented")
}
}</code></pre>
> The next noisy element is the <P: AsRef<Path>> constraint. It is needed because Rust loves exposing physical layout of bytes in memory as an interface, specifically for cases where that brings performance. In particular, the meaning of Path is not that it is some abstract representation of a file path, but that it is just literally a bunch of contiguous bytes in memory.<p>I can't understand this. Isn't this for polymorphism like what we do this:<p>```rust
fn some_function(a: impl ToString) -> String {
a.to_string();
}
```<p>What to do with memory layout? Thanks for any explanation.
I wonder. How does Rust syntax compares with <a href="https://www.hylo-lang.org/" rel="nofollow">https://www.hylo-lang.org/</a> syntax?
That also is memory safe, typesafe, and data-race-free.
Here's the cleaned up version of Rust from the OP:<p><pre><code> pub fn read(path: Path) -> Bytes {
let file = File::open(path);
let bytes = Bytes::new();
file.read_to_end(bytes);
bytes
}
</code></pre>
Here is is in raku (<a href="https://raku.org" rel="nofollow">https://raku.org</a>):<p><pre><code> sub read(Str:D $path --> Buf:D) {
$path.IO.slurp: :bin
}
</code></pre>
[the `--> Buf:D` is the raku alternative to monads]
Kinda disingenuous, you don't reskin one language in another to make an argument about syntax -- you develop a clear syntax for a given semantics. That's what rust <i>did not</i> do -- it copied c++/java-ish, and that style did not support the weight.<p>When type signatures are so complex it makes vastly more sense to separate them out,<p>Consider,<p><pre><code> read :: AsRef(Path) -> IO.Result(Vec(U8))
pub fn read(path):
inner :: &Path -> IO.Result(Vec(U8))
fn inner(path):
bytes := Vec.new()
return? file := File.open(path)
return? file.read_to_end(&! bytes)
return OK(bytes)
inner(path.as_ref())</code></pre>
<p><pre><code> "I think that most of the time when people think they have an issue with Rust’s syntax, they actually object to Rust’s semantics."
</code></pre>
You think wrong. Rust syntax is horrible because it is verbose and full of sigils
The final version is still ugly. Why `pub fn'? Why is public not the default and why do you have to specify that it's a function? Why `: type' and `-> type', why can't type go before the identifier? Why do you need `File::' and `Bytes::'? What is that question mark? Why does the last statement not need a semicolon? It's like the opposite of everything people are used to.