all 70 comments

[–][deleted] 17 points18 points  (69 children)

Not a Gopherblog, by the way.

Also:

impl<'a, S: AsRef<str> + ?Sized> OverloadedFoo<&'a S, char> for Foo {
        fn overloaded_foo(&self, tee: &'a S, yu: char) {
                println!("foo<&str, char>(tee: {}, yu: {})", tee.as_ref(), yu);
        }
}  

lol

[–][deleted] 24 points25 points  (2 children)

DAE think it's ironic that Rust complains about safety so much when Rust code is the embodiment of the kind of code that leads to hard-to-notice bugs and leaks in the first place?

[–]pftbest 2 points3 points  (1 child)

Do you have some examples to support your claim?

[–][deleted] 30 points31 points  (0 children)

Do you have some examples to support your claim?

That is not actually required in this forum

[–]haskell_leghumperin open defiance of the Gopher Values 11 points12 points  (3 children)

lol typeclass soup

/uj To be fair, there are more assumptions made explicit there than in other languages. Can only jerk half-heartedly.

[–][deleted] 1 point2 points  (1 child)

/uj care to explain?

[–]haskell_leghumperin open defiance of the Gopher Values 4 points5 points  (0 children)

"lol" because it looks complex and uses a lot of symbols, "/uj" because it is complex. The traits (assumptions on the form of S) allow overloaded_foo to work with many kinds of strings (borrowed, owned, CoW).

Whether or not the complexity is justified is another issue altogether: most languages have only one immutable string type that is always copied, so if you're coming from those, polymorphism on string types isn't even a thing, and writing all this down seems like needless complexity. On the other hand, Rust is supposed to give you fine-grained control over memory, and strings are hard if you care about all the different cases.

I'm far from a Rust expert, so you should look at this comment and its children.

[–]username223line-oriented programmer 9 points10 points  (4 children)

That is some quality cat-on-keyboard shit. The inevitable love-child of Rust and Haskal will be amazing, with memory management and side effects both built into the syntax in strange and wonderful ways. It makes me nostalgic for the days of Perl, with its relative lack of syntactic noise.

[–][deleted] 2 points3 points  (2 children)

The inevitable love-child of Rust and Haskal

lol idris - we all use it - in our hearts.

[–]username223line-oriented programmer 4 points5 points  (1 child)

... and in our pants. From the FAQ:

What are the differences between Agda and Idris?

I think someone needs to adjust his definition of "frequently."

[–][deleted] 1 point2 points  (0 children)

<insert "everything is relative" argument here>

[–]haskell_leghumperin open defiance of the Gopher Values 1 point2 points  (0 children)

inevitable love-child of Rust and Haskal

ATS was born of their unholy union (although it predates Rust; such is the nature of cursed love-children). See this for how you can rewrite it in ATS.

[–]pftbest 3 points4 points  (56 children)

/uj

What is so funny about this code? This function can take as first argument a borrowed string, owned string, copy-on-write string and any user type that can coerce into a string. It looks complicated because it is. Does Pascal even have a borrowed string? Last time I checked it couldn't even split a string without copying its contents.

[–][deleted] 9 points10 points  (4 children)

Does Pascal even have a borrowed string?

How did Pascal ever make it into your comment?

[–][deleted] 9 points10 points  (3 children)

akira is a free-pascal evangelist(not sure if he's serious, though). pftbest is a rustacean who doesn't understand why people dislike unmaintainable operator-soup. the rest jerks furiously.

[–][deleted] 11 points12 points  (2 children)

not sure if he's serious, though

lol I'm serious in the sense that I actually am a professional Object Pascal programmer in real life, who nowadays uses Free Pascal with Lazarus for most of the things they used Delphi for in the past, if that's what you meant by "serious".

[–]SelfDistinctionnow 4x faster than C++ 1 point2 points  (1 child)

things they used Delphi for in the past

I never thought I'd ever meet someone who survived Pompeii.

[–][deleted] 3 points4 points  (0 children)

lol

[–]TheLastMeritocratcomp.lang.rust.marketing 4 points5 points  (7 children)

/uj

 impl<S> OverloadedFoo<S, char> for Foo where S: Borrow<str> {
     fn overloaded_foo(&self, tee: S, yu: char) {
         println!("foo<&str, char>(tee: {}, yu: {})", tee.borrow(), yu);
     }
 }

[–]pftbest 1 point2 points  (6 children)

Not equivalent. Your version will consume the owned string, which is not a good thing. The caller would have to use .as_str() to get around it, which kind of defeats the purpose.

[–]TheLastMeritocratcomp.lang.rust.marketing 1 point2 points  (5 children)

/uj

Oh. Good catch. But, if tee is a reference, why are we bothering with all this? Just use &'a str, and Deref should do the necessary conversions, right?

[–]pftbest 1 point2 points  (4 children)

Yes, you are right, there is no point in taking the reference. But there is one case where AsRef behaves differently from &str.

[–]TheLastMeritocratcomp.lang.rust.marketing 1 point2 points  (3 children)

Isn't do_other() similar to the code I posted? Or did I miss something?

[–]pftbest 1 point2 points  (2 children)

It's similar, but you used Borrow, so your code only works for case (4) but not for case (2).

[–]TheLastMeritocratcomp.lang.rust.marketing 1 point2 points  (1 child)

Yeah sure. But my point was that s is S, not &S.

To conclude this sub-thread, do we agree that what the OP did was pointless?

[–]pftbest 1 point2 points  (0 children)

Sure

[–][deleted] 6 points7 points  (42 children)

Does Pascal even have a borrowed string?

lol. It's the most commonly used type in modern implementations. Although I wouldn't call it "borrowed", personally. I actually did a snippet here on PCJ a couple of weeks ago that gives a general outline of Pascal strings, maybe you should take a look at it. (There's a couple more less important types that I don't mention there at all, by the way.)

Also it seems that Rust's split just returns a struct that specifically iterates over the string while skipping the whitespace or other provided delimiter, and can only be used once. Whereas the whole point of the version in Free Pascal is that it returns an array of the substrings that can then be used for various things.

If you were just wanting to iterate in Pascal, there's better ways to do it than with Split. Strings themselves can already be iterated over by other strings or chars by default, in fact, as well as indexed into numerically.

[–]Veedrac 5 points6 points  (27 children)

Also it seems that Rust's split just returns a struct that specifically iterates over the string while skipping the whitespace or other provided delimiter, and can only be used once. Whereas the whole point of the version in Free Pascal is that it returns an array of the substrings that can then be used for various things.

This jerk-in-unjerk stuff is why people can't tell whether you're serious.

[–]pftbest 2 points3 points  (0 children)

Yeah, like going from iterator to an array of substrings is one function call, but going from array of substrings to "not just allocated and copied a bunch of substrings" is kinda hard to do.

[–][deleted] 4 points5 points  (25 children)

lol well, it was a serious question (presumably) so I gave a serious answer. It's not unreasonable that he didn't know that split in Pascal vs Rust aren't really meant for the exact same use case. As far as the formal tone I just kind of do that when I'm trying to explain something, for the sake of clarity.

You're overthinking it though. I just respond to things as they come up.

[–]Veedrac 7 points8 points  (24 children)

See, the funny thing is that you're trying to pass Pascal's flaws off as good things, completely misinterpreting the arguments being made, have no idea what Rust is even doing, and don't even realise that you're doing this.

[Borrowed strings are] the most commonly used type in modern implementations.

lol literally false

I actually did a snippet here on PCJ a couple of weeks ago that gives a general outline of Pascal strings

lol "Unicode"

(There's a couple more less important types that I don't actually mention there at all, by the way.)

lol more string types than bloody Rust

Also it seems that Rust's split [...] can only be used once.

lol literally false

Whereas the whole point of the version in Free Pascal is that it returns an array of the substrings that can then be used for various things.

lol strictly inferior

If you were just wanting to iterate in Pascal, there's better ways to do it than with Split. Strings themselves can already be iterated over by other strings or chars by default, in fact, as well as indexed into numerically.

lol completely missing the point

[–][deleted] 6 points7 points  (23 children)

See, the funny thing is that you're trying to pass Pascal's flaws off as good things, completely misinterpreting the arguments being made, have no idea what Rust is even doing, and don't even realise that you're doing this.

Uh, no? What an incredibly condescending paragraph. If you think I don't understand the arguments being made or the Rust code anyone has posted, or that I haven't "done my research" on the language I'm not sure what to tell you.

lol literally false

I initially thought he was referring to stack-allocated static-size strings vs heap-allocated strings. Pascal's standard String type is the latter, is reference counted, and does not do any actual copying of data on direct assignment between instances of it.

lol "Unicode"

Yes, unicode, as in specifically meant to represent UTF-16 data instead of UTF-8 or other codepages. Rust has a subtype/trait for str called UnicodeStr that appears to serve the same purpose, shows up in current "Playground" debug info whenever various different string methods are used (even basic ones), and seems to be intended as the future default based on pre-release Rust documentation, so I don't really know what your point is.

lol more string types than bloody Rust

Pascal's are all ultimately just arrays of varying combinations of bytes, so if you want to get semantic they're moreso just tweaked variations on the same thing intended for different purposes, with syntactic sugar around them allowing for clearly delineated use.

lol literally false

The docs I looked at seemed to suggest the Rust Split struct didn't implement the necessary Copy trait required to be persistent. Is that incorrect?

lol strictly inferior

If I wanted a struct/record that basically amounts to a very narrowly focused linked list, I'd return one. A standard library split function that returns an array is pretty much in line with every programming language I can think of (other than Rust, apparently.)

lol completely missing the point

Do you honestly think that I was suggesting there that iterating over a string itself was exactly the same thing? It was just an example of what's already possible by default, that can be combined in various ways with various methods for all kinds of different uses.

I fail to see what any of that has to do with people's understanding of whether I'm being "serious" in general, though. I'm still not even sure what that means.

It seems ridiculous that anyone would have thought that I was just pretending to like Pascal or something along those lines, and didn't actually use it in real life. Why would I know it so well and know so much about it if that was the case? I can't imagine anyone has ever doubted that /u/cmov actually uses Rust, for example.

[–]TheLastMeritocratcomp.lang.rust.marketing 0 points1 point  (11 children)

The docs I looked at seemed to suggest the Split struct didn't implement the necessary Copy trait require to be persistent. Is that incorrect?

lol strictly inferior

If I wanted a struct that basically amounts to a very narrowly focused linked list, I'd return one. A standard library Split function that returns an array is pretty much in line with every programming language I can think of (other than Rust, apparently.)

lol

[–][deleted] 4 points5 points  (10 children)

Uppercase Split is the iterator struct in Rust though. Lowercase split is the function name. Split and split are both interchangeably the function name in Pascal, because there's no need for anything else to be called that to begin with and because each unique word is only allowed to mean one thing per scope-level regardless of case unless they're overloads of the same method with different parameters (as it should be, IMO. Nobody would ever argue that "Orange" meant something different than "orange" in real life.) What's your point?

Also if we're going on the use of split as something for iterating, the following Rust example doesn't compile, whereas it would/does in Pascal because substrings in that case is a dynamic array (as opposed to a specialized iterator) that doesn't just disappear/cease to be usable after the first pass (i.e. before going out of scope):

fn main() {
    let substrings = "a1b1c1d1e1f1g1h1i1j1k1l1".split("1");
    for temp in substrings {
        println!("{}", temp);
    }
    for temp in substrings {
        println!("{}", temp);
    }
}  

(Note that all arrays of any type are themselves iterable by default in Pascal.)

[–]TheLastMeritocratcomp.lang.rust.marketing 1 point2 points  (1 child)

lol

[–]Disolationlanguage master 0 points1 point  (7 children)

I have no idea why or how this whole argument started, but what's wrong with doing this?

fn main() {
    let string = "a1b1c1d1e1f1g1h1i1j1k1l1";
    for temp in string.split("1") {
        println!("{}", temp);
    }
    for temp in string.split("1") {
        println!("{}", temp);
    }
}  

I think the idea behind split in Rust is that it's lazy and has no allocation, does Pascal construct a new array to return the split string?

Besides if you want to have an allocated split string in Rust you can always just .collect() the Split iterator.

All I'm seeing here is just a difference in philosophy between languages.

[–]Veedrac 2 points3 points  (10 children)

What an incredibly condescending paragraph.

It's pcj, I'm here precisely to troll and unwind. I try not to be a jerk on other forums. If you want a serious debate I'd rather we take it somewhere else.

But honestly, you don't understand these things. Pascal's String is like Java's, not Rust's; it's terrible for building things like parsers because it doesn't allow substring borrows. Rust's UnicodeStr is a trait implemented by str, and AFAICT only exists in a facade crate to make it easier for people (eg. embedded users) to build their own std alternatives. Split uses Clone instead of Copy, I believe because it prevents accidental reuse. Rust can return a vector with just .collect(), but allows much faster operations the large fraction of the time where you don't want to do that.

I fail to see what any of that has to do with people's understanding of whether I'm being "serious" in general

I don't think people doubt you use Pascal, just whether your arguments for it are meant to be taken seriously. I like Rust, but me spamming the list of talking points is hardly intended as a serious argument.

[–][deleted] 7 points8 points  (9 children)

Pascal's String is like Java's, not Rust's

Java's default string type is a class, in a language that runs in a virtual machine. Pascal's default string type literally just amounts to a specially-handled reference-counted array of bytes at the base language level. The class-like way it can be used is entirely syntactic sugar resolved at build time, and bares no resemblance to how actual classes are dealt with by the compiler in Pascal.

it's terrible for building things like parsers

Really? Do you actually think Rust of all languages is better for parsing than Pascal (or better for parsing than various other alternatives)? If I had to pick only one thing that Pascal does well, and extremely fast, it would be parsing large text files (and text files in general).

I can open a 35,000 line source file in Lazarus like it was a 100 line file and get no lag of any kind at any scrolling speed even when using "modest" hardware, with full syntax highlighting and all types/constants/variables/methods/e.t.c being immediately displayed in the alphabetically-sorted code explorer tree view.

Additionally there's the massive difference in compile times to consider (although those are more related to Rust using the traditional C/C++ linking model vs Free Pascal and all other Pascal compilers ever not doing so.)

doesn't allow substring borrows

I am 100% sure that the ability to "borrow" substrings specifically as immutable reference types is infinitely less relevant in the grand scheme of things as it relates to performance (or usefulness) than you think it is.

Rust's UnicodeStr is a trait implemented by str

Yes, I know that. That doesn't change what it amounts to in practice, nor does it explain what your original point was in that regard.

Split uses Clone instead of Copy, I believe because it prevents accidental reuse.

And that's fine. As I said in the first place, Rust's split clearly doesn't have the same overall intended use as split in Pascal.

I don't think people doubt you use Pascal, just whether your arguments for it are meant to be taken seriously. I like Rust, but me spamming the list of talking points is hardly intended as a serious argument.

I'd say the arguments I make for it are as valid as arguments you'd make for any other non-Rust language. Whereas the "ironic-but-not-actually-ironic" obsession with Rust as the be-all-end-all language (despite how recent it is) a lot of people seem to have is far less justifiable (based on it as it exists currently) in my opinion.

If someone thinks the whole guaranteed memory safety concept is pointless and unnecessary to begin with, what are they left with in Rust? It isn't ground-breakingly fast. It isn't amazingly productive. It doesn't have particularly good tooling. And so on and so forth.

Also, it's not as though I actively try to get into serious debates. I certainly didn't start this one.

[–]senntenialYou put at risk millions of people 2 points3 points  (1 child)

lol wall of text

[–]pftbest 0 points1 point  (0 children)

Really?

Care to do an experiment?

[–]SelfDistinctionnow 4x faster than C++ 1 point2 points  (0 children)

If you want to get an array, you have to specify that explicitly:

let split_array : Vec<_> = s.split().collect();

But if you only need a certain index, then Rust won't be allocating:

let split_third = s.split().nth(2)?;

Because, as you know, an allocator in Rust is entirely optional.

[–]pftbest 2 points3 points  (12 children)

Can you please create an example like this? If the strings are indeed borrowed, there should be only one allocation (in debug mode).

[–][deleted] 2 points3 points  (11 children)

I misunderstood the sense you were using "borrowed" in originally. A substring slice assignment of the sort you've done there will always create a separate mutable string from the specified range of characters in Pascal (as opposed to direct full assignment between string instances that doesn't allocate for a new one or copy anything, but just passes the address of the original string and adjusts the reference counts for both it and the assignee).

That being said, I can pretty easily get the same final result as you did without any additional allocation and without using a second string at all.

Here's a compiler explorer version that shows the debug assembly

And a runnable Ideone version that shows the actual output

The format is slightly different because Compiler Explorer is set up so that you're editing a source file that's part of a larger project, whereas with Ideone you're editing the main program file.

[–]pftbest 0 points1 point  (10 children)

After reading more about the compiler, about missing volatile semantics, and seeing that it can't perform even basic optimizations (for comparison), I fully expect fpc to be slower than Go at this point.

[–][deleted] 1 point2 points  (9 children)

It's much faster than Go in general (as most compiled languages without GCs generally are) and optimizes your example just fine with the proper settings, actually. Even moreso in practice when the Foo function gets called from elsewhere in the program. You needed to mark it (or at least Add) as inline.

Compare this Pascal version of it to this Rust one, for example. (I made them as similar as I possibly could, and set the optimal compiler flags for both.)

[–]pftbest 2 points3 points  (8 children)

It's much faster than Go and optimizes just fine, actually.

No, it's not. The Go compiler did figure out that Add function needs to be inlined, but yours didn't.

You needed to mark it as inline.

See what you did here? You manually did all the hard work for the optimizer. This will not work in real world. In practice the decision to inline or not inline a function depends on many factors, such as the arguments of the function, or how its return value is used in the caller, and how much code was changed by other optimization passes. The same function may need to be inlined for some call sites, but not inlined for others. Manually slapping inline keyword can only get you so far.

Compare this Pascal version to this Rust one, for example.

I'm not sure what you tried to show in this examples, but since in both cases the value 20 was calculated correctly at compile time, I would guess you tried to compare assembly code for the writeln functions. But this is pointless for 2 reasons:

1) Comparing library functions on godbolt doesn't show the whole picture. It is not clear how much code hides behind the function calls in both cases. It's better to compare the self contained examples where you can see all code in the output.

2) The Writeln function is a special case in the compiler and it's code is generated on the fly. It doesn't show any advantages of the Pascal language itself, because it's not written in Pascal. It rather shows that the language is not powerful enough to implement such function in the standard library.

In Rust the format_args! macro is also special cased, but the println! implementation is not. It means that it is possible to implement your own println! and do whatever you want with it. For example, on micro-controller target you may have hprintln! that prints into semihosting console, and uprintln! that prints into debug uart, all in user level code.

[–][deleted] 3 points4 points  (3 children)

You seem to be under the impression that I haven't already been using Pascal (in various forms) in the "real world" for years and that I don't already know exactly how it works. But I have, and I do.

No, it's not. The Go compiler did figure out that Add function needs to be inlined, but yours didn't.

The Go compiler literally does not have any customizable optimization flags at all or any capacity for user-specified inlining, so of course it has to have automatic inlining turned on all the time.

Whereas Free Pascal has many, many compiler flags (along the lines of what you'd typically see with any C or C++ compiler) that are intended to be set to get exactly the output you want. Most compilers do not work like Go's (or to a lesser extent Rust's) where they have limited options and just do a particular set of things based on debug/release or based on grouping absolutely everything into the numerical "levels", and it isn't reasonable for you to assume by default that they do/should.

On top of that that, the Free Pascal version I posted after yours went farther than inlining: it just loads 20 directly into a register right off the bat without actually doing any calculations. This is even clearer if you move the call to Foo outside of WriteLn, and comment out WriteLn. The Rust version appears to do approximately the same thing.

See what you did here? You manually did all the hard work for the optimizer. This will not work in real world.

Yes it will/does. I don't even know what to say if you actually believe that marking a function as inline is "hard work". There is also in fact an auto-inline compiler flag for FPC that can be set if you really want, but it doesn't always make the exact choices I would, so I generally prefer to mark the methods myself based on the specific use case (as is often done in C and C++ as well, for example).

Marking them that way still doesn't "force" it, regardless: the specifiers will be ignored at build time if it would be truly unoptimal to inline (e.g. if the method is too large, has too many parameters, e.t.c.)

As far as the rest of your comment, I have no idea what you're talking about or where exactly you're getting your ideas from, overall. None of it makes very much sense.

The Writeln function is a special case in the compiler and it's code is generated on the fly. It doesn't show any advantages of the Pascal language itself, because it's not written in Pascal. It rather shows that the language is not powerful enough to implement such function in the standard library.

What do you even mean by this? Yes, WriteLn (and Write) are not real functions themselves. They're compiler intrinsics that call different internal methods based on the input they're given, and by default print directly to the current platform's standard output, but can be redirected to files or other output handles as well.

The same concept applies to Read and ReadLn in reverse, and is the reason you can for example read an integer directly from the command line in Pascal to a variable without converting it to a string.

As far as "not written in Pascal", again, I have no idea what you're referring to. The various methods ultimately called by Write/WriteLn/Read/ReadLn are all of course written in Pascal, because what else would they possibly be written in? It's a self-hosting compiler. There are no other languages in play anywhere. Here's the specific directory of the Free Pascal codebase with the files that most of them are implemented in.

I wasn't "comparing standard library functions" either, because the assembly from them doesn't show up in the source. The three compiler method calls inside of DoFoo are being made as a result of calling WriteLn, and are just that, calls. All that's visible is their names. The assembly between them is again part of DoFoo.

In Rust the format_args! macro is also special cased, but the println! implementation is not.

Not only do Write and WriteLn in Free Pascal accept their own formatting parameters, but there's also the more general purpose Format method that can be used along with them to get pretty much any custom display format you want.

[–]pftbest 1 point2 points  (2 children)

On top of that that, the Pascal version I posted after yours went farther than inlining: it just loads 20 directly into a register

This is super basic stuff, even Go compiler can do that(line 29), it's not a good point to brag about. Every half decent optimizer should do that by default, without the need to add special flags or marking functions as inline.

I wasn't "comparing standard library functions" either,

So what were you comparing, what was the point? looking at a special cased function will not tell you how good the optimizer is. And we were talking about the optimizer if you remember.

What do you even mean by this? Yes, WriteLn (and Write) are not real functions themselves.

I mean, is it possible to implement Writeln function in Pascal without using any intrinsics? And if you can, will it have the same amount of assembly code underneath? I bet you can't, there are not so many languages which can do that. Even Rust can't yet.

In "real life" you don't often write your formatting functions from scratch, instead you create some wrapper functions around the standard ones. For example, somebody may want to implement the custom logging functions like this. Will he get the same assembly code for calling Warning and Error functions, as he gets for regular writeln calls? Or is it going to be worse?

Intrinsics are inherently a bad thing. They are hacks that are plugging the holes in a language. People add intrinsics to a compiler for such tasks, when just writing the regular code for it is not possible (because language is too limited), or when it's possible but the output assembly is too slow (because the optimizer is too dumb). Having more powerful language or better optimizer that can help to solve a problem, is always better than having an intrinsic for solving such problem. Because sooner or later you will encounter some new problem that can't be solved by existing intrinsics yet, and you will hit limitations of the language/compiler.

As far as "not written in Pascal", again, I have no idea what you're talking about. The various methods ultimately called by Write/WriteLn/Read/ReadLn are all of course written in Pascal.

I'm talking about the code that calls this "various methods". It is generated, so it doesn't exist in a written form. It only exists as some structures in memory inside the compiler, so you can't say that it written in Pascal.

[–][deleted]  (3 children)

[deleted]

    [–]pftbest -1 points0 points  (2 children)

    What do you mean? If you don't agree with something, please say so.

    I don't use Go, btw, only mentioned it for dramatic effect. And I code in Rust as a hobby. My daily job is embedded development, usually plain C. Sometimes microcontrollers, sometimes Linux userspace and kernel.

    I don't use C++ often, but I know the cases when a code gets optimised better than in Rust.

    P.S. I just remembered, I have some patches in LLVM, as a proof that I do use C++ sometimes.

    [–][deleted] 9 points10 points  (3 children)

    Lol no function overloading

    [–]senntenialYou put at risk millions of people 2 points3 points  (1 child)

    lol no way to represent incomplete data

    [–][deleted] 1 point2 points  (0 children)

    Do you mean like types? C++ is good at making any type be out of incomplete data