bio->bi_partno = bio_src->bi_partno;
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/lin...
bio->bi_partno = bio_src->bi_partno;
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/lin...
Quoting myself from https://www.reddit.com/r/programming/comments/61rh9j/curl_is... :
> For instance, the patch for CVE-2016-5420, "Incorrect reuse of client certificates", is about making sure that when you compare two structs, you don't forget to compare a field in the two structs. In C, you have to open-code this comparison. In Rust, you just stick #[deriving(Eq)] before the struct, because unlike C, Rust knows that comparison of structs is a thing that people might want to do ever. The compiler will generate a trait Eq implementation that compares all the fields, using the Eq implementations (either automatically-derived or manual) of each field, so you even get secure string compares if you define some SecureBuffer type and use it for the strings. (As a bonus, doing that also makes sure that you don't accidentally use a normal strcmp on the strings.) When you add a new field to the struct, the automatic Eq implementation on the struct will automatically compare it, too.
(Disclaimer: while I happen to like Rust, there are plenty of other languages you could also use instead—Microsoft Research even made a fork of C# designed for kernelspace. And while I am interested in writing Rust, I'm not particularly interested in telling people who are happy writing C that they should stop, just in making sure that people considering a language for a new project know what each language brings to the table.)
https://github.com/curl/curl/blob/8aee8a6a2d/lib/vtls/vtls.c#L94
and not this: https://github.com/curl/curl/blob/master/lib/vtls/vtls.c#L94
(Deliberately disabling truncation on the URL, at the cost of losing clickability. This comment isn't really about these specific links anyway, just the 'syntax' of the URL.) struct Example {
x: i32,
y: i32,
}
fn example_clone(input: &Example) -> Example {
Example {
x: input.x,
// Oops, forgot y
}
}
This fails with a following error: error[E0063]: missing field `y` in initializer of `Example`
--> src/main.rs:7:5
|
7 | Example {
| ^^^^^^^ missing `y`Also, even if somebody did insist on explicit mut-output pattern in Rust anyway, they would quickly realize that they need to initialize the value before getting a mutable reference to it - Rust NEVER allows access to uninitialized memory (other than unsafe blocks where you can trick Rust into accesing uninitialized memory and cause undefined behavior by using `std::mem::uninitialized`). The noise related to it should hint that this is not the intended way to do it. Here is a code example to clarify what I mean.
fn write2(out: &mut i32) {
*out = 2;
}
fn main() {
let mut two;
write2(&mut two);
}
And error message: error[E0381]: use of possibly uninitialized variable: `two`
--> src/main.rs:7:17
|
7 | write2(&mut two);
| ^^^ use of possibly uninitialized `two`
Fixing that requires explicitly writing a value into `two`, for instance with `let mut two = 0`, which for complex structs... yeah, you may as well return a struct directly instead of bothering with pointer output.[std::mem::uninitialized]: https://doc.rust-lang.org/std/mem/fn.uninitialized.html
It just memsets the entire thing to 0, which in Rust would
1. require unsafe{}
2. most likely be UB as the struct looks like it has a bunch of pointers which would not be nullable (e.g. arrays)
And therefore not be done at all, or rejected by reviewers if attempted.
That would've caused this code change to fail, unless I'm missing something.
The entire structure was just memset to 0, it's not like the struct was carefully initialised.
C++ or Rust might have put a 0 in the uninitialized member, but the bug would have remained.
If you don't override it, it will copy everything properly.
> Rust might have put a 0 in the uninitialized member
Rust does not do that. If a member is not initialised, it's uninitialised and in this case the structure will be rejected. Here even if you could not just have derived Clone (struct bio looks non-trivial) your hand-rolled implementation would have complained that you had not properly initialised the member.
Sure, so does memcpy or bare struct assignment in C. The problem is that trivial copy constructors are relatively rare.
> Here even if you could not just have derived Clone (struct bio looks non-trivial) your hand-rolled implementation would have complained that you had not properly initialised the member.
Right, but this is more similar to an assignment operator. DerefMut in Rust I think wouldn't have caught the bug.
Only if you don't zoom out. If you do, this is alloc/init/fill which in C++ or Rust would use a regular copy and RVO.
Typical Linus rant. There's no substantive, logical, reasoned argument in there about why C++ would be inappropriate. The closest we get is that "You invariably start using the "nice" library features of the language like STL" — God forbid the language provide a dynamically sized array, and not force you to implement it manually.
Not saying that Linux should switch from C to C++, nor that doing so wouldn't be a metric crap ton of work (it would be) and for benefits that might be hard to sell (e.g., at this point, we already have a decent vector implemented in C macros; what does switching actually gain us?) or that the kernel doesn't have special concerns (in a kernel, you have to implement malloc(), for example (and that's probably a gross simplification) and that could definitely affect a lot of things in the STL), etc. Just that the linked rant is not valid argument as to why C++ isn't a good fit.
Thing is, it's a very real issue. C++ as written by the best of programmers might be a very reasonable language for the kernel, but in practice what happens is the additional abstraction and indirection mechanisms mean it becomes very hard to decipher with any confidence code written by mediocre or merely average programmers.
And this is a real issue in practice, I saw it when I was at Google with code written by other programmers at Google, and honestly the majority of the code in the Linux kernel is at about the same level - some of it really good elegant code, more of it ugly and hacky but working, and a lot more (especially driver code) mediocre crap.
And when having to decipher and refactor the mediocre crap - because let's be honest, that's the majority of the code - I'd much rather have to deal with C than C++.
The reason this is such an issue with C++ is that - and it's been said before, but it bears repeating - C++ adds a lot of new ways to shoot yourself in the foot and it doesn't take any of the old ones away. With C, you can generally read a chunk of code and have confidence that it does what it appears to do - you can understand it in isolation. With C++, just figuring out the flow control can be a nightmare.
Rust would be a different story. I am 100% in favor of using Rust whenever possible - it has some of the same issues as C++ or any other polymorphic language in that figuring out flow control can really suck, but it provides much stronger guarantees w.r.t. how code can screw you over that make it well worth it. C++... not so much.
Linus's argument against C++ is basically akin to Dijkstra's argument against `goto`. Like any tool, it can be abused. But they've seen too many abuses for their own taste, so they ban the tool completely despite the advantages the tool may have.
Monotone uses boost, standard C++ practices, and it's much slower and more bloated than git.
The idea is that, even if git could be written in C++, with similar performance compared to the C version, C++ programmers would invariably end with something closer to monotone than git.
C programmers: you're not off the hook - malloc/free are just as bad, which is why the kernel has it's own context appropriate memory allocaters.
And of course don't forget that malloc/free (and I assume new/delete) are not safe to be used inside user space signal handlers
C++ has changed quite a bit in the last 10 years.