example
fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6];
let result: Vec<_> = linq! {
from n in numbers
where n % 2 == 0
select n * n
}.collect();
for r in result {
println!("{}", r);
}
}
fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6];
let result: Vec<_> = linq! {
from n in numbers
where n % 2 == 0
select n * n
}.collect();
for r in result {
println!("{}", r);
}
}
Looks like someone implemented this as a crate: crates.io: Rust Package Registry
yes but would b better adding this to th oficial language
Does it really have that much of an advantage over simple combinators?
let result: Vec<_> = numbers
.into_iter()
.filter(|n| n % 2 == 0)
.map(|n| n * n)
.collect();
Thr bar to adding things to the language is rather high given that we can't ever make breaking changes to it again once it has been stabilized. For something like this where an external crate can easily implement it without requiring any compiler support, it is unlikely that it will be added to the language. Things that are added to the language are generally things to which one or more of the following applies:
I don't think linq support for rust has any of these cases that apply to it. It can be done without compiler support using a proc macro, for most codebases it isn't really useful given the existence of iterator combinators I think (if anything I think linq is a workaround for missing iterator combinators in C#), and it doesn't add anything to the interoperability vocabularity as you can't expose it in the public interface of a crate.
yes because it would be easier to read and macros are expanded in compilation time, also i didn't said that i wanted to end with simple combinators
Disagree. I don't use it even in C#, preferring the method form.
Why do you think this random extra syntax is worth adding?
If Rust had something similar to LINQ, the main advantage would be making the code more straightforward and readable, almost like writing SQL queries directly inside the language, without needing to build long pipelines of map, filter, and collect. This would help developers coming from C# or corporate environments feel more at home, since declarative syntax makes it easier to understand the intent of the code without getting lost in implementation details. It could also simplify integration with databases or DSLs, because the business logic would look closer to how we naturally think about data. The risk is that it would add more complexity to the compiler and might drift away from Rust’s philosophy of keeping everything as zero-cost abstractions and giving explicit control to the programmer. In short, it would be a trade-off: more convenience and readability versus the possibility of losing some of Rust’s simplicity and performance focus.
I object to the idea that SQL is a more natural way to think about data than MapReduce. I don’t think it’s obvious that one is more readable than the other, and at least for me I find the LINQ syntax to be an interruption while reading non-LINQ code, requiring me to switch my “mental parser” to the DSL and back. I realize this is all highly subjective, and you may be right that most people coming from SQL or C# would find this more approachable than iterator combinators…but that’s not some kind of universal.
LINQ is a also pipeline of operators that desugar to a bunch of map/filter/collect-like calls. The only difference is syntactic.
Merely supporting a similar syntax does not automatically create integrations between the two, especially when databases generally require the SQL query to be sent as is while LINQ naturally desugars it into method calls or objects that then need to be translated back into SQL strings (and this AFAIK relies on some very permissive operator overloading rules that are a non-goal for Rust)
LINQ to Entities is one thing, but LINQ to SQL is a total abomination. The way it works in C# (as far as I understand - and it's entirely possible that I understood it wrong or that they changed it since I've last looked over a decade ago) is:
Rust does not need this madness. Rust is better off with macros like SQLx' query!.
So I'm actually going to disagree with your characterization of this as "madness", because what I came to this thread to say is, I think the more interesting language design goal is not LINQ-ish syntax, but the language-level capability of taking a complex query expressed as Rust, e.g.
let idle_accounts: Table<_> = people_table.join(last_access_table)
.filter(|row| row.last_access < date(2020, 1, 1))
.collect();
and turning it into an AST that can be manipulated by a library, either at compile or at run time, and pushed down into a query execution engine. I've seen C++ "expression template" libraries that do exactly this. I don't think the way it's done in C++ is something we'd want to copy, but I don't see why something proc-macro-ish -- probably working at a later stage of translation than the current proc macros -- shouldn't be allowed to do it.
Isn't the standard iterator pattern in Rust essentially expression templates under the hood, implemented quite similarly to C++ (modulo differences between templates and traits)?
Expression templates can see into lambdas. Rust's iterator combinators can't. That's the biggest difference that I know about.
Lambda are unique types in C++, and closures are unique types in Rust. So are normal functions in Rust (but not in C++, where their type is that of the matching function pointer).
You misunderstand - the expression template equivalent of filter(|row| row.last_access < date(2020, 1, 1)) wouldn't be written with a C++ native lambda at all. I don't remember exactly how it would look, but it would wind up with row.last_access < date(...) being parsed into an unevaluated expression tree that the library could manipulate just like it could manipulate the containing expression.
Being able to do this opens up all sorts of optimization opportunities, such as recognizing that the filter expression only looks at fields of the last_access_table and therefore it can be hoisted to before the join.