0

I've seen a lot of usage of Box::new in Rust language.

https://doc.rust-lang.org/book/ch15-01-box.html

fn main() {
    let b = Box::new(5);
    println!("b = {}", b);
}

However, I think this is a bit redundant, especially, in the code like this:

enum List {
    Cons(i32, Box<List>),
    Nil,
}

use crate::List::{Cons, Nil};

fn main() {
    let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
}

Now, I want to write in a concise way using Macro as we've seen as vec!

https://doc.rust-lang.org/book/ch19-06-macros.html?highlight=vec#declarative-macros-with-macro_rules-for-general-metaprogramming

let v: Vec<u32> = vec![1, 2, 3];

so that with box! macro, we could write:

fn main() {
    let b = box!(5);
    println!("b = {}", b);
}

or

fn main() {
    let list = Cons(1, box!(Cons(2, box!(Cons(3, box!(Nil))))));
}

My question is

  1. Is there any reason that box! macro is not popular even it's used frequently in Rust?

  2. What is the proper way to define the macro? Is there any points of attention?

KenSmooth
  • 275
  • 1
  • 10
  • Please edit your first question - it's a bit unclear what you are asking. – at54321 Jan 19 '22 at 19:03
  • Macros are not intended to just be a shorthand, they're designed to cover cases that necessitate special syntax, overloading, reducing boiler-plate, etc. The `vec!` macro significantly improves readability, reduces code, and uses special syntax. A `box!` macro on the other hand does not significantly reduce code, improve readability, or require special syntax. *"Don't use macros where a function would suffice"* - this advice spans across languages. – kmdreko Jan 19 '22 at 19:51
  • @kmdreko I think your estimation is too much subjective and more like wrong. See my answer. – KenSmooth Jan 19 '22 at 21:02
  • 2
    @KenSmooth the `box_syntax` feature has an uncertain future. While it does exist and is available on nightly, it could be stabilized or it could be removed completely (you'll see the first item for the issue is "how to phase it out"). The most promising angle for keeping it has been to provide placement-new functionality, which would warrant a keyword since it then would have additional semantic meaning. But either way, the advice against a macro is still appropriate. Promoting the feature simply for it's lesser character count is a weak argument. – kmdreko Jan 19 '22 at 21:23
  • @kmdreko Well, anyone can see it's on nightly, and whatever you insist, I think it's a proof that Box:new is considered to be redundant. Plus, I'm originally a JS/TS functional programmer and never use macro in my code and understand your advice; however, for some unknown reason Rust guys prefer to use macro for vec! or println!, and in terms of the situation, if Vec:new() is defined as vec!, the reasonable approach should be Box::new == boxed! or whatever. I mean if you are so against macro, your criticism shall go against vec!, too. Why don't you insist that. – KenSmooth Jan 19 '22 at 21:37
  • 2
    *"I think it's a proof that Box:new is considered to be redundant."* - then it'll blow your mind that `box` has been around since before the Rust `1.0` release, but when the time came around the language team decided not to stabilize it and haven't in the many years since then. Either way, that's for the `box` keyword but your question is about a potential `box!` macro. As I've said in my first comment, the `vec!` macro pulls its weight but a `box!` macro offers no benefit over `Box::new`. If you really want to save 4 characters, you can make your own macro as answered by at54321. – kmdreko Jan 19 '22 at 22:01
  • You repeatedly mention `box!` macro has no benefit over `Box::new`, but as I post the `Cons` list expample, it's obvious the latter syntax is redundant and does have an issue for readability. It's totally fine you personally feel the extra 4 characters does not bother you; however, it's not only character but also a structure with `::`, and I don't think your personal feeling does not justify to prevent to create another `vec!` like macro. I never appreciate the underestimation of the conciseness of syntax, which is much more important factor for coding than you feel. – KenSmooth Jan 19 '22 at 22:30
  • 1
    To be honest, I'm fine with not using the `vec![]` macro too and just use `Vec::from([...])`. The problem is that this syntax was not possible until const generics were stabilized, and the `vec![]` macro is very widely used now. – Chayim Friedman Jan 19 '22 at 23:01
  • Personally, I feel lucky for myself to start Rust now because too many important features have been missing until now. Even considering with the safe memory management as a system language, the mechanism and syntax is pretty much scattered, and I think they could've done better. – KenSmooth Jan 19 '22 at 23:30
  • @KenSmooth I think it's quite the opposite: the mechanism and the syntax are much better than almost any other language around, and I would choose Rust only for them, even without considering safety. That being said, this is certainly not the place to talk about that. – Chayim Friedman Jan 19 '22 at 23:40

3 Answers3

4

This is a bit subjective, but vectors are used far more often, I believe. And the vec! macro saves you a lot more code compared to the box! macro you are referring to.

As for the macro implementation, you can write something like this:

macro_rules! bx {
    ($e:expr) => {
        Box::new($e)
    };
}

Here is an example usage:

let b = Box::new(3);
let m = bx!(7);

Note that I named it bx! and not box!, as the latter is a reserved word in Rust.

at54321
  • 8,726
  • 26
  • 46
  • 3
    Why not make it a function if you really don't like `Box::new`? E.g. `fn bx (v: T) -> Box:: { Box::new (v) }` – Jmb Jan 20 '22 at 09:04
  • 1
    @Jmb I just answered the question. I'm not saying creating such a macro is worth it. But if it is all about brevity, you are right. An inlined function should be practically the same as a macro (but with no `!`). The only potential slight advantage of a macro that I can think of is the option to avoid importing it in every file you use it. See [this](https://stackoverflow.com/a/70011610/15602349). I am not saying that's a strong argument, though. – at54321 Jan 20 '22 at 11:55
3

however, for some unknown reason Rust guys prefer to use macro for vec! or println!

I suggest asking this the other way around: why couldn't vec! and println! be functions? And the answer is, both are variadic (can take variable number of arguments) and there are no variadic functions in Rust. It's much worse for println!, whose arguments can have different types too; and constraints on those types depend on the first argument! Try writing down a type for println as a function, and you'll find it would be much less usable and less efficient.

The problem vec! solves is smaller. But as Chayim Friedman's comment says, until quite recently you couldn't even have had

Vec::from([1,2,3])

if there were no vec! macro. I think the best you could have done was

Vec::from(&[1,2,3])

As Chayim points out, this is strictly worse than vec!.

Of course, Box::new doesn't have this problem at all. So it's a function just because it didn't have to be a macro.

but as I post the Cons list expample, it's obvious the latter syntax is redundant and does have an issue for readability

In that case box! is no less redundant; instead a way to get rid of redundancy is

fn cons(head: i32, tail: List) -> List {
    List::Cons(head, Box::new(tail))
}

and then

let list = cons(1, cons(2, cons(3, Nil)));

Or even a list! macro and

let list = list![1,2,3];
Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
-1

Thanks to @at54321, I googled and found

https://doc.rust-lang.org/beta/unstable-book/language-features/box-syntax.html

#![feature(box_syntax)]

fn main() {
    let b = box 5;
}

So, already there.

KenSmooth
  • 275
  • 1
  • 10
  • Yes. The `box` *keyword* (which is still **unstable**) will be easier on the eye than a macro, for sure. – at54321 Jan 19 '22 at 22:19
  • 3
    Don't use that. [The box syntax is to-be-removed feature that will probably not ever be stabilized, especially not in the current form](https://github.com/rust-lang/rust/issues/49733), _because_ there's nothing wrong with `Box::new()`. – Chayim Friedman Jan 19 '22 at 22:46
  • I noticed that for sure. The section includes https://github.com/rust-lang/rust/issues/49733 and the thread answered my question well. Since I do believe `Box::new()` has problem to write and read in complicated codes, I will use Macro. – KenSmooth Jan 19 '22 at 22:53
  • @KenSmooth Can I at least suggest using a plain function instead of a macro, as in Jmb's comment to the other answer? – Alexey Romanov Jan 21 '22 at 15:03