Heater
Posts: 13701
Joined: Tue Jul 17, 2012 3:02 pm

The Rust debate.

Sat Sep 28, 2019 9:04 am

From time to time I have mentioned Rust here which has resulted in some negative responses from old hand C/C++ programmers. I found this somewhat surprising as one would expect engineers with such experience to be very interested in tools that help them get their job done correctly. One such comment came here: https://www.raspberrypi.org/forums/view ... 1#p1542355. I thought it better to spin off a new thread to reply on:

jcyr,
Good luck. Rust has got to be the most counter intuitive language I've ever encountered. "foobar".to_string() just looks absurd, and folks will give you three different ways of doing it right! In my experience it doesn't quite deliver the safety, clarity, and performance it promises.
Not much luck required I hope. Having looked into Rust for some weeks now and created some not entirely trivial programs with it it's clear that whatever I may be able to dream of can actually be implemented in Rust. Be that compact high speed code in remote devices or "web stuff" in the servers. I cannot see it being worse than the mess of C++, Javascript and Python we have had in similar ventures. Whilst offering, or at least promising very useful advantages.

As for "foobar".to_string() or String::from("Hello, world!"), well, that may look a bit odd to a C/C++ guy. Programming languages often look odd to those not familiar with them and especially if they are steeped in some other language. C and C++ have their absurdities too, one tends to not see them after much experience.

I'm curious about you "counter intuitive" statement. Whilst the syntax may be somewhat different Rust is for the most part a very normal structured programming language in the spirit of Algol, Pascal, Ada, C. Intuitively the same.

Where things diverge is in the fanatical checking Rust does of types, object lifetimes and legal aliases. The "borrow checker". The semantics of that are clear enough but working with it is sometimes difficult. On the other hand the borrow checker is only enforcing rules that you should enforce on yourself to create memory and thread safe code in C or C++.

As Rust creator Graydon Hoare says: "The semantics is the interesting part. The syntax is, really, about the last concern."

I'm curious as to what you mean by "...doesn't quite deliver the safety, clarity, and performance...". In what ways exactly?

My experience so far, admittedly limited, is that Rust's memory safety guarantees work very well, the code I have written is at least as readable as C++ and its performance has matched and exceeded that of C and C++. What Am I missing here?
Memory in C++ is a leaky abstraction .

hippy
Posts: 6107
Joined: Fri Sep 09, 2011 10:34 pm
Location: UK

Re: The Rust debate.

Sat Sep 28, 2019 11:52 am

Heater wrote:
Sat Sep 28, 2019 9:04 am
As for "foobar".to_string() or String::from("Hello, world!"), well, that may look a bit odd to a C/C++ guy.
Looks odd to me and I'm not a C/C++ guy. It''s not so odd in its syntax or semantics, but that it suggests one would have to convert from, what appears to be a string, to a string, to be able to use it.

I am guessing that a string not being a string and needing to be converted to a string, is what was meant by being counter intuitive but it's all rather lacking context and I'm not familiar with Rust.

I looked at Rust and I'm sure it's a perfectly good language but, on first glance, it seemed to me to be like C but not quite C and I couldn't see the point of it. I felt the same when I looked at Go.

But I'll leave this 'language war' thread to others to engross.

Heater
Posts: 13701
Joined: Tue Jul 17, 2012 3:02 pm

Re: The Rust debate.

Sat Sep 28, 2019 12:24 pm

Well, in C++ a string is not a string either. When you write:

Code: Select all

string greeting = "Hello";
You have a C style string of null terminated bytes in read only memory being copied into a new C++ string object on the stack.

You can of course write:

Code: Select all

char* greeting = "Hello"
Which is yet another kind of string.

Arguably C++ is has a nicer syntax for that kind of thing. Rust does not like to do automatic, implicit, conversions like that. For very good reasons. So one ends up with something like:

Code: Select all

let greeting = String::from("Hello, ");
As for the point of Rust... in short the main point for me is that it disallows all kinds of code that would otherwise be potentially hard to find bugs. No use of unititialized data, no dereferencing null pointers or pointers to freed memory, no out of bounds access to arrays, no threads writing to the same data willy-nilly and causing chaos, etc.

All that would be desirable but useless if it were not for the fact that Rust can do it whilst also producing code that performs as well as C/C++.

That emphasis on safety and correctness has already earned a place for me. I converted a couple of thousand lines of C#, not written by me, to Rust and in the process Rust found a couple of subtle bugs that caused data corruption.

Once again hoping for not a language war. A reasoned discussion of features and their desirability/usability would be nice. If I'm gong to invest time in building stuff that leads to a dead end I'd rather find out about it before I start!
Memory in C++ is a leaky abstraction .

jcyr
Posts: 463
Joined: Sun Apr 23, 2017 1:31 pm
Location: Atlanta

Re: The Rust debate.

Sat Sep 28, 2019 2:00 pm

Heater wrote:
Sat Sep 28, 2019 12:24 pm
Once again hoping for not a language war. A reasoned discussion of features and their desirability/usability would be nice. If I'm gong to invest time in building stuff that leads to a dead end I'd rather find out about it before I start!
There is so much already written about it, I would only be repeating what others have already said.

Personally, I'm an engineer, not a linguist... I like to get things done. I bristle at your characterization of C++ as a mess. Avoiding a language war should not start with such characterizations.

Would you characterize the plethora of half baked, often poorly documented crates the same way?
It's um...uh...well it's kinda like...and it's got a bit of...

Heater
Posts: 13701
Joined: Tue Jul 17, 2012 3:02 pm

Re: The Rust debate.

Sun Sep 29, 2019 5:41 am

jcyr,
There is so much already written about it, I would only be repeating what others have already said.
No doubt there is. Perhaps I have not seen it yet. Anyway what I'm interested in what you and others here have said and they said it. See below.
Personally, I'm an engineer, not a linguist... I like to get things done.
Same here. I'm no compiler writer or language designer/theorist, far from it. I find programming language design and the history of programming languages fascinating but at the end of the day I want a tool to use. Preferably tools that are not going to require me to learn more and cause bigger headaches than the actual problem I want to tackle.
I bristle at your characterization of C++ as a mess. Avoiding a language war should not start with such characterizations.
That is a fair point. I said "mess", you said "looks absurd", these are far to emotive terms for a discussion of technical merits.
Would you characterize the plethora of half baked, often poorly documented crates the same way?
That is a legitimate concern. Certainly a public "warehouse" full of use contributed code could be full of inefficient buggy, badly documented modules. Or even if they do work they can change in the future and break your code. This concern is the same with node.js modules, or CPAN in Perl or PIP in Python etc.

A couple of thoughts on that:

0) That registry of crates is not the language. It's a whole other subject.

1) You don't have to use them. No more than you have to use whatever libraries and code snippets you can find for C/C++ or whatever language by scouring the internet.

2) Anything you are going to incorporate in your project should be vetted to some degree. If there is no or bad documentation then don't use it. If it has no tests and no examples don't use it. You can always read the code if you are really fussy. You can check the repos for activity, issues, bug fixes. You can evaluate the skill and dedication of the authors.

3) At the end of the day I suspect having such library repositories is better than having nothing. Personally I don't have the time or skill to create everything for myself. It makes no sense for everyone to be recreating the wheel for themselves over and over again. As such having a package system like crates or NPM or PIP etc is a very good idea.

Now, back to my question: the claim was "...doesn't quite deliver the safety, clarity, and performance...". So what I'm fishing for is:

1) Where/how did you find that the safety claims were not met?

2) Where/how did you find performance was lacking?

These are down to Earth practical things, if they are going to catch me out I'd rather know about it now. Before I dig too big a hole for myself.

We might skip "clarity" for now. The clarity of anything is so much dependent on the reader.
Memory in C++ is a leaky abstraction .

User avatar
PeterO
Posts: 5086
Joined: Sun Jul 22, 2012 4:14 pm

Re: The Rust debate.

Sun Sep 29, 2019 6:36 am

Some general points....

I think it's important to remember that when you compare languages one key question to bear in mind is "How long have they existed?".

It's unfair to judge a new language in the same way you would an mature one.

New ones can change since they don't yet have the inertia of a large code base of libraries and applications. It's unlikely the designers get everything right in V1.0 so expect some churn. They can also suffer from "not invented here" and try to address the same issues as other new languages.

Mature ones offer stability, a large amount of libraries and examples to draw upon (of varying quality maybe) and the confidence that they aren't going to vanish over night, plus they've had lots of bugs fixed already. But, they can have been "enhanced" to the point where they have diverged from their original roots (Yes, I'm looking at you C++ :-) )

PeterO
Discoverer of the PI2 XENON DEATH FLASH!
Interests: C,Python,PIC,Electronics,Ham Radio (G0DZB),1960s British Computers.
"The primary requirement (as we've always seen in your examples) is that the code is readable. " Dougie Lawson

jahboater
Posts: 4785
Joined: Wed Feb 04, 2015 6:38 pm

Re: The Rust debate.

Sun Sep 29, 2019 10:11 am

PeterO wrote:
Sun Sep 29, 2019 6:36 am
Some general points....

I think it's important to remember that when you compare languages one key question to bear in mind is "How long have they existed?".

It's unfair to judge a new language in the same way you would an mature one.

New ones can change since they don't yet have the inertia of a large code base of libraries and applications. It's unlikely the designers get everything right in V1.0 so expect some churn. They can also suffer from "not invented here" and try to address the same issues as other new languages.

Mature ones offer stability, a large amount of libraries and examples to draw upon (of varying quality maybe) and the confidence that they aren't going to vanish over night, plus they've had lots of bugs fixed already. But, they can have been "enhanced" to the point where they have diverged from their original roots (Yes, I'm looking at you C++ :-) )

PeterO
Well said.
PeterO wrote:
Sun Sep 29, 2019 6:36 am
It's unfair to judge a new language in the same way you would an mature one.
That's true of course, however, when I choose a language to implement a new project, the simple fact that the new language is unstable is itself a negative point. This makes it hard for new languages to become accepted.

We (well I wont) will see in 50 years time if Rust is in such widespread use as C is now.
Last edited by jahboater on Sun Sep 29, 2019 10:36 am, edited 1 time in total.

User avatar
Gavinmc42
Posts: 3936
Joined: Wed Aug 28, 2013 3:31 am

Re: The Rust debate.

Sun Sep 29, 2019 10:25 am

I tried to do some OpenGL in Rust last week.
Ran into Cargo issues and remembered why I gave up on Rust years ago.
Great language but Cargo ruined it for me.
Working behind a firewall means offline Cargo

Is there a Cargo for dummies tutorial?
I'm dancing on Rainbows.
Raspberries are not Apples or Oranges

jahboater
Posts: 4785
Joined: Wed Feb 04, 2015 6:38 pm

Re: The Rust debate.

Sun Sep 29, 2019 10:32 am

It difficult to compare two languages for speed.
Only if both languages are supported by the same compiler is it reasonable.

I am skeptical about claims that xxx safe language is faster than C.
Sure certain language features might allow static checks, but that is still no faster than C which by default does no run-time checks either.

How Rust can do run-time integer overflow checks with zero overhead defies me.

jahboater
Posts: 4785
Joined: Wed Feb 04, 2015 6:38 pm

Re: The Rust debate.

Sun Sep 29, 2019 10:43 am

PeterO wrote:
Sun Sep 29, 2019 6:36 am
But, they can have been "enhanced" to the point where they have diverged from their original roots (Yes, I'm looking at you C++ :-) )
Take a look at "modern" Fortran. If you grew up with Fortran IV like I did, you wont see much that is familiar.

C seems to be ultra stable, presumably because of the enormous existing code base.
The latest standard C18 is really only bug fixes.

The new key words in C99 and C11 were all carefully introduced in such a way as not to invalidate existing code.
Last edited by jahboater on Sun Sep 29, 2019 10:47 am, edited 1 time in total.

Heater
Posts: 13701
Joined: Tue Jul 17, 2012 3:02 pm

Re: The Rust debate.

Sun Sep 29, 2019 10:45 am

In case anyone is unsure, Rust is not "unstable".

Rust was started in 2006. That is a long time ago now. Certainly it underwent significant changes before arriving at version 1.0 in 2015.

I don't expect breaking changes in the future, or at leas no more so than other languages, even those that have ISO standards.

Of course the extra strict compile time checking of Rust would make such changes immediately apparent and prevent undefined behaviors.

It's a judgement call of course, the ice looks thick enough for me now. If you see what I mean.
Memory in C++ is a leaky abstraction .

jahboater
Posts: 4785
Joined: Wed Feb 04, 2015 6:38 pm

Re: The Rust debate.

Sun Sep 29, 2019 10:53 am

Heater wrote:
Sun Sep 29, 2019 10:45 am
Of course the extra strict compile time checking of Rust would make such changes immediately apparent and prevent undefined behaviors.
Yes. And that's fine, the safe option. Better than Python 2/3.

New features in C are introduced in such a way that existing code will still compile.
Starting with C99 when they added the boolean type.
The new key word is _Bool and you have to explicitly include "stdbool.h" to get the "bool", "true", and "false" keywords.

Presumably because there are countless existing projects where the programmers have defined their own "bool" data type.

Ditto for all the other new keywords in C11

Heater
Posts: 13701
Joined: Tue Jul 17, 2012 3:02 pm

Re: The Rust debate.

Sun Sep 29, 2019 11:25 am

jahboater,
It difficult to compare two languages for speed. Only if both languages are supported by the same compiler is it reasonable.
Quite so, from a purely language definition point of view. As it happens Rust uses the LLVM back end so a direct comparison with C/C++ and others is possible. There is very likely to be a front end for GCC soon enough.

Traditionally languages did not use the same compiler. ALGOL was compiled by an ALGOL compiler, Fortran by a Fortran compiler. Same for PL/M, Coral and a ton of others.
I am skeptical about claims that xxx safe language is faster than C.
Me too. That would be a big claim, if anyone ever made it, that would take a lot of substantiating.

There are however reasons to believe that the strict "anti-aliasing" rules of Rust could allow optimizers to make optimizations that they cannot in C/C++. Simply because they have more information about how data is used at run time. Much the same reason the LLVM optimizer writers tell us to use "const" as much as possible in C++.

My understanding is that the current Rust front end does not implement these possibilities for LLVM yet.
Sure certain language features might allow static checks, but that is still no faster than C which by default does no run-time checks either.
Quite so. I might naively guess that given the same algorithm, written similarly in C and Rust then LLVM would spit out the same instruction sequences. No gain to be expected there.
How Rust can do run-time integer overflow checks with zero overhead defies me.
Good question.

It turns out those overflow checks are disabled by default in release builds. One can of course enable or disable them in release or debug builds. My experiments with this show pretty small performance impacts when enabling overflow checks. Needs more testing.

I guess in simple cases the compiler can tell overflow is not possible. If you have just promoted a couple of bytes to 32 bit integers and added them there is no chance of overflow. Also there is no need to check overflow of loop counters when you are using iterators, the compilers is generating those ranges it knows it's not going to overflow.
Memory in C++ is a leaky abstraction .

Heater
Posts: 13701
Joined: Tue Jul 17, 2012 3:02 pm

Re: The Rust debate.

Sun Sep 29, 2019 12:25 pm

Gavinmc42,

That is a shame. Cargo is very easy to use and very useful.

Of course not having access to the net makes that rather difficult.

I don't know about dummies but the "Hello Cargo" chapter of the Rust book is about as simple as it can get:
https://doc.rust-lang.org/book/ch01-03-hello-cargo.html

Next up you need to know how to use some crate in your project. Just add it to the Cargo.toml file, for example:

Code: Select all

[dependencies]
time = "0.1.12"
More typically I just get the latest versions:

Code: Select all

[dependencies]
time = "*"
Which is probably not what you want to do if you are fussy about reproducibility.

That is about all I use so far. But there is a lot more when you need it. For example I switch on overflow checking in release builds:

Code: Select all

[profile.release]
overflow-checks = true
See the reference: https://doc.rust-lang.org/cargo/reference/index.html

You sure you can't use cargo trough a hot spot on your phone or some such?
Memory in C++ is a leaky abstraction .

jahboater
Posts: 4785
Joined: Wed Feb 04, 2015 6:38 pm

Re: The Rust debate.

Sun Sep 29, 2019 7:50 pm

Heater wrote:
Sun Sep 29, 2019 11:25 am
Traditionally languages did not use the same compiler. ALGOL was compiled by an ALGOL compiler, Fortran by a Fortran compiler. Same for PL/M, Coral and a ton of others.
Yes. Long ago Fortran was popular for running benchmarks and comparing mainframes. So the manufacturers made sure their Fortran compilers offered very high optimization. The IBM VS Fortran compiler was stunning and always produced faster code than I could in assembler (long before C could). That was not true for most other languages, so the conclusion was that Fortran was very fast. In fact it was no faster than Algol, just that the compilers were better.
Heater wrote:
Sun Sep 29, 2019 11:25 am
There are however reasons to believe that the strict "anti-aliasing" rules of Rust could allow optimizers to make optimizations that they cannot in C/C++. Simply because they have more information about how data is used at run time. Much the same reason the LLVM optimizer writers tell us to use "const" as much as possible in Good question.
C++.
You mean this option offered by GCC and most other compilers including MSVC?
-fstrict-aliasing
Allow the compiler to assume the strictest aliasing rules applicable to the language
being compiled. For C (and C++), this activates optimizations based on the type of
expressions. In particular, an object of one type is assumed never to reside at the
same address as an object of a different type, unless the types are almost the same.
For example, an "unsigned int" can alias an "int", but not a "void*" or a "double". The
character type may alias any other type.
From your comments about integer overflow checks, it all sounds similar at the end of the day to C (you can turn on overflow checks with a compiler option). I do wonder if two languages with similar objectives are, over time, likely to end up with similar solutions.

jcyr
Posts: 463
Joined: Sun Apr 23, 2017 1:31 pm
Location: Atlanta

Re: The Rust debate.

Sun Sep 29, 2019 8:36 pm

jahboater wrote:
Sun Sep 29, 2019 7:50 pm
From your comments about integer overflow checks, it all sounds similar at the end of the day to C (you can turn on overflow checks with a compiler option). I do wonder if two languages with similar objectives are, over time, likely to end up with similar solutions.
I don't think Rust allows you to turn off array bounds checking, at least you couldn't the last time I fought with rustc. There is significant overhead to checking every indexed access. Compare a Rust coded 2-D FFT with one coded in C.
It's um...uh...well it's kinda like...and it's got a bit of...

jahboater
Posts: 4785
Joined: Wed Feb 04, 2015 6:38 pm

Re: The Rust debate.

Sun Sep 29, 2019 8:44 pm

jcyr wrote:
Sun Sep 29, 2019 8:36 pm
I don't think Rust allows you to turn off array bounds checking, at least you couldn't the last time I fought with rustc. There is significant overhead to checking every indexed access. Compare a Rust coded 2-D FFT with one coded in C.
I believe there is a space overhead too - needed in some cases to store the array size.

User avatar
paddyg
Posts: 2395
Joined: Sat Jan 28, 2012 11:57 am
Location: UK

Re: The Rust debate.

Sun Sep 29, 2019 11:04 pm

I think there is an issue that Rust is hard to get to grips with. Personally I think it's worth it, but there were times over the first few weeks when it was very frustrating!

The bounds checking is a case in point - It's easy to make a 2D array and iterate over it in such a way that the bounds need to be checked. But once you get the hang of Rust you wouldn't do that. (OK well I might!). There is unsafe unchecked indexing if you need it, but generally the compiler can tell whether it's possible for the index to go outside the array and only check if it is.
also https://groups.google.com/forum/?hl=en-GB&fromgroups=#!forum/pi3d

jahboater
Posts: 4785
Joined: Wed Feb 04, 2015 6:38 pm

Re: The Rust debate.

Sun Sep 29, 2019 11:24 pm

Heater,

I thought your problem with C was the possibility of undefined behavior?

See this list in the Rust doct:
https://doc.rust-lang.org/reference/beh ... fined.html

The list for C is longer, but it is complete and definitive (and is included in the ISO standard).
The Rust list (they say) is incomplete and its extent is unknown.
Perhaps that's because its a young language.

I see the Rust list includes some problems similar to C (such as de-referencing a NULL pointer) and other things new to Rust.

User avatar
Gavinmc42
Posts: 3936
Joined: Wed Aug 28, 2013 3:31 am

Re: The Rust debate.

Mon Sep 30, 2019 1:37 am

I don't know about dummies but the "Hello Cargo" chapter of the Rust book is about as simple as it can get:
https://doc.rust-lang.org/book/ch01-03-hello-cargo.html
Thanks Heater, finally I get it, Cargo is like a combo "make" and "git/svn".

Any good IDEs that understand Cargo/Rust?
I just discovered Code::Blocks does the OpenGL etc heavy lifting for C++.
I'm dancing on Rainbows.
Raspberries are not Apples or Oranges

Heater
Posts: 13701
Joined: Tue Jul 17, 2012 3:02 pm

Re: The Rust debate.

Mon Sep 30, 2019 2:00 am

jahboater,
I thought your problem with C was the possibility of undefined behavior?
I have more problems with C of course but let's go with the almost certainty of undefined behavior in any non-trivial program for now.
See this list in the Rust doct:....The list for C is longer, but it is complete and definitive...the Rust list includes some problems similar to C
You have skipped over a very important detail there. The keyword "unsafe".

That list includes all the the things that are considered undefined behavior when the unsafe keyword is used with function definitions, code blocks and elsewhere. See: https://doc.rust-lang.org/reference/unsafe-blocks.html

The idea is that there are times when you really have to do things the compiler cannot check. A classic example is accessing peripheral registers and memory areas when dealing with hardware. The Foreign Function Interface when you want to link with your code with C and other languages has to be unsafe, there is no way the compiler can know what goes on in there.

You may also want unsafe for building linked lists and such where the Rust concept of ownership gets in the way.

The idea is that the use of the unsafe keyword clearly indicates "dangerous" areas of code and that the programmer is saying "trust me, I know what I'm doing". In general those areas of code are a very small part of a program and with the unsafe keyword on them the programmer knows exactly what he has to verify carefully. More importantly anyone else maintaining the code or just taking the code into use knows what to look for.
Memory in C++ is a leaky abstraction .

Heater
Posts: 13701
Joined: Tue Jul 17, 2012 3:02 pm

Re: The Rust debate.

Mon Sep 30, 2019 2:06 am

Gavinmc42,
Any good IDEs that understand Cargo/Rust?
I use Visual Studio Code with the RLS extesion else vim.

https://github.com/rust-lang/rls-vscode

https://github.com/rust-lang/rust.vim
Memory in C++ is a leaky abstraction .

User avatar
paddyg
Posts: 2395
Joined: Sat Jan 28, 2012 11:57 am
Location: UK

Re: The Rust debate.

Mon Sep 30, 2019 7:27 am

Gavinmc42 were you using the --offline option in cargo? I think that's a relatively recent feature so there might be quirks you need to Google round.

I use vscode and do everything on my laptop (writing code that just works unaltered or cross compiles on different platforms or OSs seems a strong point of cargo)
also https://groups.google.com/forum/?hl=en-GB&fromgroups=#!forum/pi3d

User avatar
Gavinmc42
Posts: 3936
Joined: Wed Aug 28, 2013 3:31 am

Re: The Rust debate.

Mon Sep 30, 2019 8:13 am

Gavinmc42 were you using the --offline option in cargo
When I last looked at Cargo/Rust years ago I don't remember an offline version.
Same sort of deal with Node.JS, my Pi's needed an internet connection.
Manuals installs were just possible if only a few dependencies, too painful otherwise.

Done behind a firewall and headless too.

So C/C++ with dependancies and Rust/Cargo, Node.JS etc got ruled out for local IoT stuff.
So I ended up with PiCore, Micropython, shell script and then Ultibo.

Now I have fibre to the house at home, so most new stuff done/learned there now.

And wow has VSCode changed, plugins for all the languages I have been using recently.
Pony, Zig, Nim, GLSL...…., wonder how compiler aware it is.
Does VSC run on Pi4? Seem to remember it being slow as.

Been catching up on Node/JS, Espruiro….
Baremetal JS with low.js?
But so many Cargos for Rust now, seems very popular now.
writing code that just works unaltered or cross compiles on different platforms or OSs seems a strong point of cargo
Or free pascal ;)
I'm dancing on Rainbows.
Raspberries are not Apples or Oranges

jahboater
Posts: 4785
Joined: Wed Feb 04, 2015 6:38 pm

Re: The Rust debate.

Mon Sep 30, 2019 8:24 am

Heater wrote:
Mon Sep 30, 2019 2:00 am
You have skipped over a very important detail there. The keyword "unsafe".
when the unsafe keyword is used with function definitions, code blocks and elsewhere. See: https://doc.rust-lang.org/reference/unsafe-blocks.html
Aah OK, I misread it.

I took this:
Rust code is incorrect if it exhibits any of the behaviors in the following list. This includes code within unsafe blocks and unsafe functions.
to mean "includes but is not limited to".

Return to “Other programming languages”